/* Standard includes. */
#include <stdlib.h>
#include <string.h>

//
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "StackMacros.h"

#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE

/* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting
functions but without including stdio.h here. */
#if (configUSE_STATS_FORMATTING_FUNCTIONS == 1)
#include <stdio.h>
#endif /* configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) */

#if (configUSE_PREEMPTION == 0)
/* If the cooperative scheduler is being used then a yield should not be
	performed just because a higher priority task has been woken. */
#define taskYIELD_IF_USING_PREEMPTION()
#else
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
#endif

// ucNotifyState
#define taskNOT_WAITING_NOTIFICATION ((uint8_t)0)
#define taskWAITING_NOTIFICATION ((uint8_t)1)
#define taskNOTIFICATION_RECEIVED ((uint8_t)2)

// 填到任务栈里的字节
#define tskSTACK_FILL_BYTE (0xa5U)

// 
#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE (((configSUPPORT_STATIC_ALLOCATION == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1)) || (portUSING_MPU_WRAPPERS == 1))
#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ((uint8_t)0)
#define tskSTATICALLY_ALLOCATED_STACK_ONLY ((uint8_t)1)
#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ((uint8_t)2)

// 任务状态
#define tskBLOCKED_CHAR ('B')
#define tskREADY_CHAR ('R')
#define tskDELETED_CHAR ('D')
#define tskSUSPENDED_CHAR ('S')

//
#ifdef portREMOVE_STATIC_QUALIFIER
#define static
#endif

// 某些方法自己实现，还是在特定控制器的port文件里实现
#if (configUSE_PORT_OPTIMISED_TASK_SELECTION == 0)

//
#define taskRECORD_READY_PRIORITY(uxPriority)  \
	{                                          \
		if ((uxPriority) > uxTopReadyPriority) \
		{                                      \
			uxTopReadyPriority = (uxPriority); \
		}                                      \
	}

// 
#define taskSELECT_HIGHEST_PRIORITY_TASK()                               \
{                                                                    \
	UBaseType_t uxTopPriority = uxTopReadyPriority;                  \
                                                                         \
	/* Find the highest priority queue that contains ready tasks. */ \
	while (listLIST_IS_EMPTY(&(pxReadyTasksLists[uxTopPriority])))   \
	{                                                                \
		configASSERT(uxTopPriority);                                 \
		--uxTopPriority;                                             \
	}                                                                \
                                                                         \
	listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[uxTopPriority])); \
	uxTopReadyPriority = uxTopPriority;							\
}

/*-----------------------------------------------------------*/

#define taskRESET_READY_PRIORITY(uxPriority)
#define portRESET_READY_PRIORITY(uxPriority, uxTopReadyPriority)

#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

// 将任务的优先级，记录到uxTopReadyPriority中
#define taskRECORD_READY_PRIORITY(uxPriority) portRECORD_READY_PRIORITY(uxPriority, uxTopReadyPriority)

/*-----------------------------------------------------------*/

// 将下一个就绪的最高优先级任务，赋值给pxCurrentTCB
#define taskSELECT_HIGHEST_PRIORITY_TASK()                                              \
	{                                                                                   \
		UBaseType_t uxTopPriority;                                                      \
                                                                                        \
		/* Find the highest priority list that contains ready tasks. */                 \
		portGET_HIGHEST_PRIORITY(uxTopPriority, uxTopReadyPriority);                    \
		configASSERT(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[uxTopPriority])) > 0); \
		listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[uxTopPriority])); \
	}

/*-----------------------------------------------------------*/

// 判断并去掉uxPriority优先级在uxTopReadyPriority中的标志位
#define taskRESET_READY_PRIORITY(uxPriority)                                               \
	{                                                                                      \
		if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[(uxPriority)])) == (UBaseType_t)0) \
		{                                                                                  \
			portRESET_READY_PRIORITY((uxPriority), (uxTopReadyPriority));                  \
		}                                                                                  \
	}

#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

/*-----------------------------------------------------------*/

//
// 交换pxDelayedTaskList和pxOverflowDelayedTaskList，待研究xNumOfOverflows++
#define taskSWITCH_DELAYED_LISTS()                                                \
	{                                                                             \
		List_t *pxTemp;                                                           \
                                                                                  \
		/* 交换时pxDelayedTaskList必须是空的 */ 									\
		configASSERT((listLIST_IS_EMPTY(pxDelayedTaskList)));                     \
                                                                                  \
		pxTemp = pxDelayedTaskList;                                               \
		pxDelayedTaskList = pxOverflowDelayedTaskList;                            \
		pxOverflowDelayedTaskList = pxTemp;                                       \
		xNumOfOverflows++;                                                        \
		prvResetNextTaskUnblockTime();                                            \
	}

// 将任务放到就绪列表中
// 修改uxTopReadyPriority的值，将TCB的xStateListItem插入到就绪列表尾部
#define prvAddTaskToReadyList(pxTCB)                                                       \
	traceMOVED_TASK_TO_READY_STATE(pxTCB);                                                 \
	taskRECORD_READY_PRIORITY((pxTCB)->uxPriority);                                        \
	vListInsertEnd(&(pxReadyTasksLists[(pxTCB)->uxPriority]), &((pxTCB)->xStateListItem)); \
	tracePOST_MOVED_TASK_TO_READY_STATE(pxTCB)
/*-----------------------------------------------------------*/

// 获取tcb
#define prvGetTCBFromHandle(pxHandle) (((pxHandle) == NULL) ? (TCB_t *)pxCurrentTCB : (TCB_t *)(pxHandle))

//
#if (configUSE_16_BIT_TICKS == 1)
#define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000U
#else
#define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL
#endif

// TCB
typedef struct tskTaskControlBlock
{
	volatile StackType_t *pxTopOfStack; 		// 栈顶指针

	#if (portUSING_MPU_WRAPPERS == 1)
	xMPU_SETTINGS xMPUSettings; //
	#endif

	ListItem_t xStateListItem;				  	// 状态列表项，用于和任务状态列表关联
	ListItem_t xEventListItem;				  	// 事件列表项
	UBaseType_t uxPriority;					  	// 任务优先级，0表示最低
	StackType_t *pxStack;					  	// 栈开始位置指针
	char pcTaskName[configMAX_TASK_NAME_LEN]; 	// 任务名称

	#if (portSTACK_GROWTH > 0)
	StackType_t *pxEndOfStack;
	#endif

	#if (portCRITICAL_NESTING_IN_TCB == 1)
	UBaseType_t uxCriticalNesting;
	#endif

	#if (configUSE_TRACE_FACILITY == 1)
	UBaseType_t uxTCBNumber; 
	UBaseType_t uxTaskNumber;
	#endif

	#if (configUSE_MUTEXES == 1)
	UBaseType_t uxBasePriority;
	UBaseType_t uxMutexesHeld;
	#endif

	#if (configUSE_APPLICATION_TASK_TAG == 1)
	TaskHookFunction_t pxTaskTag;
	#endif

	#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0)
	void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS];
	#endif

	#if (configGENERATE_RUN_TIME_STATS == 1)
	uint32_t ulRunTimeCounter;
	#endif

	#if (configUSE_NEWLIB_REENTRANT == 1)
	struct _reent xNewLib_reent;
	#endif

	#if (configUSE_TASK_NOTIFICATIONS == 1)
	volatile uint32_t ulNotifiedValue;
	volatile uint8_t ucNotifyState;
	#endif

	#if (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0)
	uint8_t ucStaticallyAllocated;
	#endif

	#if (INCLUDE_xTaskAbortDelay == 1)
	uint8_t ucDelayAborted;
	#endif

} tskTCB;

//
typedef tskTCB TCB_t;

// 当前任务控制块
PRIVILEGED_DATA TCB_t *volatile pxCurrentTCB = NULL;

// 就绪和阻塞的任务列表
PRIVILEGED_DATA static List_t pxReadyTasksLists[configMAX_PRIORITIES]; // 就绪任务列表数组，每个优先级的任务一个列表 
PRIVILEGED_DATA static List_t xDelayedTaskList1;					   // 延时任务列表，由于判断延时的tick值会过期，所以需要两个列表轮流使用
PRIVILEGED_DATA static List_t xDelayedTaskList2;					   //
PRIVILEGED_DATA static List_t *volatile pxDelayedTaskList;			   // 当前使用的延时列表
PRIVILEGED_DATA static List_t *volatile pxOverflowDelayedTaskList;	   //
PRIVILEGED_DATA static List_t xPendingReadyList;					   // 当调度挂起时已经就绪的任务，调度恢复后，将会移到就绪列表

#if (INCLUDE_vTaskDelete == 1)
PRIVILEGED_DATA static List_t xTasksWaitingTermination; 				// 已经被删除，但内存还未释放的任务
PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = (UBaseType_t)0U;
#endif

#if (INCLUDE_vTaskSuspend == 1)
PRIVILEGED_DATA static List_t xSuspendedTaskList; 						// 当前被挂起的任务
#endif

// 其它私有变量
PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = (UBaseType_t)0U;	// 当前任务数量？
PRIVILEGED_DATA static UBaseType_t uxTaskNumber = (UBaseType_t)0U;						// 和上面的区别？
PRIVILEGED_DATA static volatile TickType_t xTickCount = (TickType_t)0U;					// tick值
PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;		// 已就绪的最高优先级
PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE;					// 调度是否运行
PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = (UBaseType_t)0U;			//
PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE;						//
PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = (BaseType_t)0;				//
PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = (TickType_t)0U; 		// 调度开始前初始化为 portMAX_DELAY
PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL;						  		/*< Holds the handle of the idle task */

// 调度是否被挂起
PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = (UBaseType_t)pdFALSE;

#if (configGENERATE_RUN_TIME_STATS == 1)
PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL;
PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL;
#endif

#if (configCHECK_FOR_STACK_OVERFLOW > 0)
extern void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName);
#endif

#if (configUSE_TICK_HOOK > 0)
extern void vApplicationTickHook(void);
#endif

#if (configSUPPORT_STATIC_ALLOCATION == 1)
extern void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize);
#endif

// 任务是否被挂起
#if (INCLUDE_vTaskSuspend == 1)
static BaseType_t prvTaskIsTaskSuspended(const TaskHandle_t xTask) PRIVILEGED_FUNCTION;
#endif

// 创建第一个任务前自动调用
static void prvInitialiseTaskLists(void) PRIVILEGED_FUNCTION;

// 空闲任务的宏
static portTASK_FUNCTION_PROTO(prvIdleTask, pvParameters);

//
#if (INCLUDE_vTaskDelete == 1)
static void prvDeleteTCB(TCB_t *pxTCB) PRIVILEGED_FUNCTION;
#endif

// 只被空闲任务使用
static void prvCheckTasksWaitingTermination(void) PRIVILEGED_FUNCTION;

// 添加当前任务到延时列表
static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely) PRIVILEGED_FUNCTION;

// 
#if (configUSE_TRACE_FACILITY == 1)
static UBaseType_t prvListTasksWithinSingleList(TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState) PRIVILEGED_FUNCTION;
#endif

//
#if (INCLUDE_xTaskGetHandle == 1)
static TCB_t *prvSearchForNameWithinSingleList(List_t *pxList, const char pcNameToQuery[]) PRIVILEGED_FUNCTION;
#endif

//
#if ((configUSE_TRACE_FACILITY == 1) || (INCLUDE_uxTaskGetStackHighWaterMark == 1))
static uint16_t prvTaskCheckFreeStackSpace(const uint8_t *pucStackByte) PRIVILEGED_FUNCTION;
#endif

//
#if (configUSE_TICKLESS_IDLE != 0)

static TickType_t prvGetExpectedIdleTime(void) PRIVILEGED_FUNCTION;

#endif

//
static void prvResetNextTaskUnblockTime(void);

//
#if ((configUSE_TRACE_FACILITY == 1) && (configUSE_STATS_FORMATTING_FUNCTIONS > 0))
static char *prvWriteNameToBuffer(char *pcBuffer, const char *pcTaskName) PRIVILEGED_FUNCTION;
#endif

//
static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,
								 const char *const pcName,
								 const uint32_t ulStackDepth,
								 void *const pvParameters,
								 UBaseType_t uxPriority,
								 TaskHandle_t *const pxCreatedTask,
								 TCB_t *pxNewTCB,
								 const MemoryRegion_t *const xRegions) PRIVILEGED_FUNCTION;

// 添加新任务到就绪列表
static void prvAddNewTaskToReadyList(TCB_t *pxNewTCB) PRIVILEGED_FUNCTION;

//
#if (configSUPPORT_STATIC_ALLOCATION == 1)

TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
							   const char *const pcName,
							   const uint32_t ulStackDepth,
							   void *const pvParameters,
							   UBaseType_t uxPriority,
							   StackType_t *const puxStackBuffer,
							   StaticTask_t *const pxTaskBuffer)
{
	TCB_t *pxNewTCB;
	TaskHandle_t xReturn;

	configASSERT(puxStackBuffer != NULL);
	configASSERT(pxTaskBuffer != NULL);

	if ((pxTaskBuffer != NULL) && (puxStackBuffer != NULL))
	{
		/* The memory used for the task's TCB and stack are passed into this function - use them. */
		pxNewTCB = (TCB_t *)pxTaskBuffer;
		pxNewTCB->pxStack = (StackType_t *)puxStackBuffer;

#if (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0)
		{
			/* Tasks can be created statically or dynamically, so note this task was created statically in case the task is later deleted. */
			pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
		}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

		prvInitialiseNewTask(pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL);
		prvAddNewTaskToReadyList(pxNewTCB);
	}
	else
	{
		xReturn = NULL;
	}

	return xReturn;
}
#endif

#if (portUSING_MPU_WRAPPERS == 1)

BaseType_t xTaskCreateRestricted(const TaskParameters_t *const pxTaskDefinition, TaskHandle_t *pxCreatedTask)
{
	TCB_t *pxNewTCB;
	BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;

	configASSERT(pxTaskDefinition->puxStackBuffer);

	if (pxTaskDefinition->puxStackBuffer != NULL)
	{
		/* Allocate space for the TCB.  Where the memory comes from depends
			on the implementation of the port malloc function and whether or
			not static allocation is being used. */
		pxNewTCB = (TCB_t *)pvPortMalloc(sizeof(TCB_t));

		if (pxNewTCB != NULL)
		{
			/* Store the stack location in the TCB. */
			pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer;

			/* Tasks can be created statically or dynamically, so note
				this task had a statically allocated stack in case it is
				later deleted.  The TCB was allocated dynamically. */
			pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY;

			prvInitialiseNewTask(pxTaskDefinition->pvTaskCode,
								 pxTaskDefinition->pcName,
								 (uint32_t)pxTaskDefinition->usStackDepth,
								 pxTaskDefinition->pvParameters,
								 pxTaskDefinition->uxPriority,
								 pxCreatedTask, pxNewTCB,
								 pxTaskDefinition->xRegions);

			prvAddNewTaskToReadyList(pxNewTCB);
			xReturn = pdPASS;
		}
	}

	return xReturn;
}

#endif /* portUSING_MPU_WRAPPERS */

// 动态分配内存下的任务创建
#if (configSUPPORT_DYNAMIC_ALLOCATION == 1)

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
					   const char *const pcName,
					   const uint16_t usStackDepth,
					   void *const pvParameters,
					   UBaseType_t uxPriority,
					   TaskHandle_t *const pxCreatedTask)
{
	TCB_t *pxNewTCB;
	BaseType_t xReturn;

	// 
	#if (portSTACK_GROWTH > 0)
	{
		pxNewTCB = (TCB_t *)pvPortMalloc(sizeof(TCB_t));

		if (pxNewTCB != NULL)
		{
			pxNewTCB->pxStack = (StackType_t *)pvPortMalloc((((size_t)usStackDepth) * sizeof(StackType_t)));

			if (pxNewTCB->pxStack == NULL)
			{
				/* Could not allocate the stack.  Delete the allocated TCB. */
				vPortFree(pxNewTCB);
				pxNewTCB = NULL;
			}
		}
	}
	#else 
	{
		StackType_t *pxStack;

		// 分配任务栈的内存空间
		pxStack = (StackType_t *)pvPortMalloc((((size_t)usStackDepth) * sizeof(StackType_t)));
		if (pxStack != NULL)
		{
			// 分配TCB的内存
			pxNewTCB = (TCB_t *)pvPortMalloc(sizeof(TCB_t));
			if (pxNewTCB != NULL)
			{
				pxNewTCB->pxStack = pxStack;
			}
			else
			{
				vPortFree(pxStack);
			}
		}
		else
		{
			pxNewTCB = NULL;
		}
	}
	#endif /* portSTACK_GROWTH */

	if (pxNewTCB != NULL)
	{
		#if (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0)
		{
			pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
		}
		#endif

		prvInitialiseNewTask(pxTaskCode, pcName, (uint32_t)usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL);
		prvAddNewTaskToReadyList(pxNewTCB);
		xReturn = pdPASS;
	}
	else
	{
		xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
	}

	return xReturn;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

// 初始化新任务
static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,
								 const char *const pcName,
								 const uint32_t ulStackDepth,
								 void *const pvParameters,
								 UBaseType_t uxPriority,
								 TaskHandle_t *const pxCreatedTask,
								 TCB_t *pxNewTCB,
								 const MemoryRegion_t *const xRegions)
{
	StackType_t *pxTopOfStack;
	UBaseType_t x;

	#if (portUSING_MPU_WRAPPERS == 1)
	{
		BaseType_t xRunPrivileged;
		if ((uxPriority & portPRIVILEGE_BIT) != 0U)
		{
			xRunPrivileged = pdTRUE;
		}
		else
		{
			xRunPrivileged = pdFALSE;
		}
		uxPriority &= ~portPRIVILEGE_BIT;
	}
	#endif

	/* Avoid dependency on memset() if it is not required. */
	#if ((configCHECK_FOR_STACK_OVERFLOW > 1) || (configUSE_TRACE_FACILITY == 1) || (INCLUDE_uxTaskGetStackHighWaterMark == 1))
	{
		(void)memset(pxNewTCB->pxStack, (int)tskSTACK_FILL_BYTE, (size_t)ulStackDepth * sizeof(StackType_t));
	}
	#endif

	// 
	#if (portSTACK_GROWTH < 0)
	{
		pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t)1);
		pxTopOfStack = (StackType_t *)(((portPOINTER_SIZE_TYPE)pxTopOfStack) & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); 
		configASSERT((((portPOINTER_SIZE_TYPE)pxTopOfStack & (portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK) == 0UL));
	}
	#else
	{
		pxTopOfStack = pxNewTCB->pxStack;

		// 检查
		configASSERT((((portPOINTER_SIZE_TYPE)pxNewTCB->pxStack & (portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK) == 0UL));

		// 
		pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t)1);
	}
	#endif

	// 存储任务名称到TCB中
	for (x = (UBaseType_t)0; x < (UBaseType_t)configMAX_TASK_NAME_LEN; x++)
	{
		pxNewTCB->pcTaskName[x] = pcName[x];
		if (pcName[x] == 0x00)
		{
			break;
		}
	}
	pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN - 1] = '\0';

	// 任务优先级检查和赋值 
	if (uxPriority >= (UBaseType_t)configMAX_PRIORITIES)
	{
		uxPriority = (UBaseType_t)configMAX_PRIORITIES - (UBaseType_t)1U;
	}
	pxNewTCB->uxPriority = uxPriority;

	#if (configUSE_MUTEXES == 1)
	{
		pxNewTCB->uxBasePriority = uxPriority;
		pxNewTCB->uxMutexesHeld = 0;
	}
	#endif

	// 初始化列表项
	vListInitialiseItem(&(pxNewTCB->xStateListItem));
	vListInitialiseItem(&(pxNewTCB->xEventListItem));

	/* Set the pxNewTCB as a link back from the ListItem_t.  This is so we can get back to	the containing TCB from a generic item in a list. */
	listSET_LIST_ITEM_OWNER(&(pxNewTCB->xStateListItem), pxNewTCB);

	/* Event lists are always in priority order. */
	listSET_LIST_ITEM_VALUE(&(pxNewTCB->xEventListItem), (TickType_t)configMAX_PRIORITIES - (TickType_t)uxPriority);
	listSET_LIST_ITEM_OWNER(&(pxNewTCB->xEventListItem), pxNewTCB);

	#if (portCRITICAL_NESTING_IN_TCB == 1)
	{
		pxNewTCB->uxCriticalNesting = (UBaseType_t)0U;
	}
	#endif

	#if (configUSE_APPLICATION_TASK_TAG == 1)
	{
		pxNewTCB->pxTaskTag = NULL;
	}
	#endif

	#if (configGENERATE_RUN_TIME_STATS == 1)
	{
		pxNewTCB->ulRunTimeCounter = 0UL;
	}
	#endif

	#if (portUSING_MPU_WRAPPERS == 1)
	{
		vPortStoreTaskMPUSettings(&(pxNewTCB->xMPUSettings), xRegions, pxNewTCB->pxStack, ulStackDepth);
	}
	#else
	{
		(void)xRegions;
	}
	#endif

	#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0)
	{
		for (x = 0; x < (UBaseType_t)configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++)
		{
			pxNewTCB->pvThreadLocalStoragePointers[x] = NULL;
		}
	}
	#endif

	#if (configUSE_TASK_NOTIFICATIONS == 1)
	{
		pxNewTCB->ulNotifiedValue = 0;
		pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	#endif

	#if (configUSE_NEWLIB_REENTRANT == 1)
	{
		_REENT_INIT_PTR((&(pxNewTCB->xNewLib_reent)));
	}
	#endif

	#if (INCLUDE_xTaskAbortDelay == 1)
	{
		pxNewTCB->ucDelayAborted = pdFALSE;
	}
	#endif

	//
	#if (portUSING_MPU_WRAPPERS == 1)
	{
		pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged);
	}
	#else
	{
		pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);
	}
	#endif

	//
	if ((void *)pxCreatedTask != NULL)
	{
		*pxCreatedTask = (TaskHandle_t)pxNewTCB;
	}
}

//
static void prvAddNewTaskToReadyList(TCB_t *pxNewTCB)
{
	// 临界区
	taskENTER_CRITICAL();
	{
		uxCurrentNumberOfTasks++;
		if (pxCurrentTCB == NULL)
		{
			//
			pxCurrentTCB = pxNewTCB;
			if (uxCurrentNumberOfTasks == (UBaseType_t)1)
			{
				// 说明这是创建的第一个任务
				prvInitialiseTaskLists();
			}
		}
		else
		{
			// 调度还没开始的话，如果它是最高优先级的任务，把它设置为当前任务
			if (xSchedulerRunning == pdFALSE)
			{
				if (pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority)
				{
					pxCurrentTCB = pxNewTCB;
				}
			}
		}

		uxTaskNumber++;

		#if (configUSE_TRACE_FACILITY == 1)
		{
			pxNewTCB->uxTCBNumber = uxTaskNumber;
		}
		#endif

		traceTASK_CREATE(pxNewTCB);

		// 
		prvAddTaskToReadyList(pxNewTCB);

		portSETUP_TCB(pxNewTCB);
	}
	taskEXIT_CRITICAL();

	// 如果新建任务的优先级，高于当前正在运行的任务，则它应该运行
	if (xSchedulerRunning != pdFALSE)
	{	
		if (pxCurrentTCB->uxPriority < pxNewTCB->uxPriority)
		{
			taskYIELD_IF_USING_PREEMPTION();
		}
	}
}

//
#if (INCLUDE_vTaskDelete == 1)

void vTaskDelete(TaskHandle_t xTaskToDelete)
{
	TCB_t *pxTCB;

	taskENTER_CRITICAL();
	{
		/* If null is passed in here then it is the calling task that is being deleted. */
		pxTCB = prvGetTCBFromHandle(xTaskToDelete);

		/* Remove task from the ready list. */
		if (uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
		{
			taskRESET_READY_PRIORITY(pxTCB->uxPriority);
		}

		/* Is the task waiting on an event also? */
		if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL)
		{
			(void)uxListRemove(&(pxTCB->xEventListItem));
		}

		/* Increment the uxTaskNumber also so kernel aware debuggers can
			detect that the task lists need re-generating.  This is done before
			portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
			not return. */
		uxTaskNumber++;

		if (pxTCB == pxCurrentTCB)
		{
			/* A task is deleting itself.  This cannot complete within the
				task itself, as a context switch to another task is required.
				Place the task in the termination list.  The idle task will
				check the termination list and free up any memory allocated by
				the scheduler for the TCB and stack of the deleted task. */
			vListInsertEnd(&xTasksWaitingTermination, &(pxTCB->xStateListItem));

			/* Increment the ucTasksDeleted variable so the idle task knows
				there is a task that has been deleted and that it should therefore
				check the xTasksWaitingTermination list. */
			++uxDeletedTasksWaitingCleanUp;

			/* The pre-delete hook is primarily for the Windows simulator,
				in which Windows specific clean up operations are performed,
				after which it is not possible to yield away from this task -
				hence xYieldPending is used to latch that a context switch is
				required. */
			portPRE_TASK_DELETE_HOOK(pxTCB, &xYieldPending);
		}
		else
		{
			--uxCurrentNumberOfTasks;
			prvDeleteTCB(pxTCB);

			/* Reset the next expected unblock time in case it referred to
				the task that has just been deleted. */
			prvResetNextTaskUnblockTime();
		}

		traceTASK_DELETE(pxTCB);
	}
	taskEXIT_CRITICAL();

	/* Force a reschedule if it is the currently running task that has just been deleted. */
	if (xSchedulerRunning != pdFALSE)
	{
		if (pxTCB == pxCurrentTCB)
		{
			configASSERT(uxSchedulerSuspended == 0);
			portYIELD_WITHIN_API();
		}
	}
}
#endif

//
#if (INCLUDE_vTaskDelayUntil == 1)

void vTaskDelayUntil(TickType_t *const pxPreviousWakeTime, const TickType_t xTimeIncrement)
{
	TickType_t xTimeToWake;
	BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

	configASSERT(pxPreviousWakeTime);
	configASSERT((xTimeIncrement > 0U));
	configASSERT(uxSchedulerSuspended == 0);

	vTaskSuspendAll();
	{
		/* Minor optimisation.  The tick count cannot change in this block. */
		const TickType_t xConstTickCount = xTickCount;

		/* Generate the tick time at which the task wants to wake. */
		xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;

		if (xConstTickCount < *pxPreviousWakeTime)
		{
			/* The tick count has overflowed since this function was
				lasted called.  In this case the only time we should ever
				actually delay is if the wake time has also	overflowed,
				and the wake time is greater than the tick time.  When this
				is the case it is as if neither time had overflowed. */
			if ((xTimeToWake < *pxPreviousWakeTime) && (xTimeToWake > xConstTickCount))
			{
				xShouldDelay = pdTRUE;
			}
		}
		else
		{
			/* The tick time has not overflowed.  In this case we will
				delay if either the wake time has overflowed, and/or the
				tick time is less than the wake time. */
			if ((xTimeToWake < *pxPreviousWakeTime) || (xTimeToWake > xConstTickCount))
			{
				xShouldDelay = pdTRUE;
			}
		}

		/* Update the wake time ready for the next call. */
		*pxPreviousWakeTime = xTimeToWake;

		if (xShouldDelay != pdFALSE)
		{
			traceTASK_DELAY_UNTIL(xTimeToWake);

			/* prvAddCurrentTaskToDelayedList() needs the block time, not
				the time to wake, so subtract the current tick count. */
			prvAddCurrentTaskToDelayedList(xTimeToWake - xConstTickCount, pdFALSE);
		}
	}
	xAlreadyYielded = xTaskResumeAll();

	/* Force a reschedule if xTaskResumeAll has not already done so, we may
		have put ourselves to sleep. */
	if (xAlreadyYielded == pdFALSE)
	{
		portYIELD_WITHIN_API();
	}
}

#endif /* INCLUDE_vTaskDelayUntil */

//
#if (INCLUDE_vTaskDelay == 1)
void vTaskDelay(const TickType_t xTicksToDelay)
{
	BaseType_t xAlreadyYielded = pdFALSE;

	// 延时为0的话，仅仅触发一个重新调度
	if (xTicksToDelay > (TickType_t)0U)
	{
		configASSERT(uxSchedulerSuspended == 0);
		vTaskSuspendAll();
		{
			traceTASK_DELAY();
			prvAddCurrentTaskToDelayedList(xTicksToDelay, pdFALSE);
		}
		xAlreadyYielded = xTaskResumeAll();
	}

	// 重新调度
	if (xAlreadyYielded == pdFALSE)
	{
		portYIELD_WITHIN_API();
	}
}
#endif

//
#if ((INCLUDE_eTaskGetState == 1) || (configUSE_TRACE_FACILITY == 1))

eTaskState eTaskGetState(TaskHandle_t xTask)
{
	eTaskState eReturn;
	List_t *pxStateList;
	const TCB_t *const pxTCB = (TCB_t *)xTask;

	configASSERT(pxTCB);

	if (pxTCB == pxCurrentTCB)
	{
		eReturn = eRunning;
	}
	else
	{
		taskENTER_CRITICAL();
		{
			pxStateList = (List_t *)listLIST_ITEM_CONTAINER(&(pxTCB->xStateListItem));
		}
		taskEXIT_CRITICAL();

		if ((pxStateList == pxDelayedTaskList) || (pxStateList == pxOverflowDelayedTaskList))
		{
			eReturn = eBlocked;
		}

#if (INCLUDE_vTaskSuspend == 1)
		else if (pxStateList == &xSuspendedTaskList)
		{
			if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) == NULL)
			{
				eReturn = eSuspended;
			}
			else
			{
				eReturn = eBlocked;
			}
		}
#endif

#if (INCLUDE_vTaskDelete == 1)
		else if ((pxStateList == &xTasksWaitingTermination) || (pxStateList == NULL))
		{
			eReturn = eDeleted;
		}
#endif

		else
		{
			eReturn = eReady;
		}
	}

	return eReturn;
}
#endif /* INCLUDE_eTaskGetState */

// 获取任务的优先级
#if (INCLUDE_uxTaskPriorityGet == 1)
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask)
{
	TCB_t *pxTCB;
	UBaseType_t uxReturn;

	taskENTER_CRITICAL();
	{
		pxTCB = prvGetTCBFromHandle(xTask);
		uxReturn = pxTCB->uxPriority;
	}
	taskEXIT_CRITICAL();

	return uxReturn;
}
#endif /* INCLUDE_uxTaskPriorityGet */

// (中断下)获取任务的优先级
#if (INCLUDE_uxTaskPriorityGet == 1)
UBaseType_t uxTaskPriorityGetFromISR(TaskHandle_t xTask)
{
	TCB_t *pxTCB;
	UBaseType_t uxReturn, uxSavedInterruptState;

	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		pxTCB = prvGetTCBFromHandle(xTask);
		uxReturn = pxTCB->uxPriority;
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptState);

	return uxReturn;
}
#endif

// 设置任务优先级
#if (INCLUDE_vTaskPrioritySet == 1)
void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority)
{
	TCB_t *pxTCB;
	UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry;
	BaseType_t xYieldRequired = pdFALSE;

	// 确保新设置的值在有效范围内
	configASSERT((uxNewPriority < configMAX_PRIORITIES));
	if (uxNewPriority >= (UBaseType_t)configMAX_PRIORITIES)
	{
		uxNewPriority = (UBaseType_t)configMAX_PRIORITIES - (UBaseType_t)1U;
	}

	taskENTER_CRITICAL();
	{
		// 获取TCB
		pxTCB = prvGetTCBFromHandle(xTask);

		traceTASK_PRIORITY_SET(pxTCB, uxNewPriority);

		// 获取任务原来的优先级
		#if (configUSE_MUTEXES == 1)
		{
			uxCurrentBasePriority = pxTCB->uxBasePriority;
		}
		#else
		{
			uxCurrentBasePriority = pxTCB->uxPriority;
		}
		#endif

		// 原值与新设置值不同
		if (uxCurrentBasePriority != uxNewPriority)
		{
			// 新值优先级更高
			if (uxNewPriority > uxCurrentBasePriority)
			{
				if (pxTCB != pxCurrentTCB)
				{
					if (uxNewPriority >= pxCurrentTCB->uxPriority)
					{
						xYieldRequired = pdTRUE;
					}
				}
			}
			else if (pxTCB == pxCurrentTCB)
			{
				xYieldRequired = pdTRUE;
			}

			/* Remember the ready list the task might be referenced from
				before its uxPriority member is changed so the
				taskRESET_READY_PRIORITY() macro can function correctly. */
			uxPriorityUsedOnEntry = pxTCB->uxPriority;

			#if (configUSE_MUTEXES == 1)
			{
				/* Only change the priority being used if the task is not
					currently using an inherited priority. */
				if (pxTCB->uxBasePriority == pxTCB->uxPriority)
				{
					pxTCB->uxPriority = uxNewPriority;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				/* The base priority gets set whatever. */
				pxTCB->uxBasePriority = uxNewPriority;
			}
			#else
			{
				pxTCB->uxPriority = uxNewPriority;
			}
			#endif

			/* Only reset the event list item value if the value is not being used for anything else. */
			if ((listGET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem)) & taskEVENT_LIST_ITEM_VALUE_IN_USE) == 0UL)
			{
				listSET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem), ((TickType_t)configMAX_PRIORITIES - (TickType_t)uxNewPriority)); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
			}

			/* If the task is in the blocked or suspended list we need do
				nothing more than change it's priority variable. However, if
				the task is in a ready list it needs to be removed and placed
				in the list appropriate to its new priority. */
			if (listIS_CONTAINED_WITHIN(&(pxReadyTasksLists[uxPriorityUsedOnEntry]), &(pxTCB->xStateListItem)) != pdFALSE)
			{
				/* The task is currently in its ready list - remove before adding
					it to it's new ready list.  As we are in a critical section we
					can do this even if the scheduler is suspended. */
				if (uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
				{
					/* It is known that the task is in its ready list so
						there is no need to check again and the port level
						reset macro can be called directly. */
					portRESET_READY_PRIORITY(uxPriorityUsedOnEntry, uxTopReadyPriority);
				}
				prvAddTaskToReadyList(pxTCB);
			}

			if (xYieldRequired != pdFALSE)
			{
				taskYIELD_IF_USING_PREEMPTION();
			}

			/* Remove compiler warning about unused variables when the port optimised task selection is not being used. */
			(void)uxPriorityUsedOnEntry;
		}
	}
	taskEXIT_CRITICAL();
}
#endif /* INCLUDE_vTaskPrioritySet */

// 任务挂起
#if (INCLUDE_vTaskSuspend == 1)
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
{
	TCB_t *pxTCB;

	taskENTER_CRITICAL();
	{
		// 获取TCB
		pxTCB = prvGetTCBFromHandle(xTaskToSuspend);

		traceTASK_SUSPEND(pxTCB);

		/* Remove task from the ready/delayed list and place in the suspended list. */
		if (uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
		{
			taskRESET_READY_PRIORITY(pxTCB->uxPriority);
		}

		/* Is the task waiting on an event also? */
		if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL)
		{
			(void)uxListRemove(&(pxTCB->xEventListItem));
		}

		vListInsertEnd(&xSuspendedTaskList, &(pxTCB->xStateListItem));
	}
	taskEXIT_CRITICAL();

	if (xSchedulerRunning != pdFALSE)
	{
		/* Reset the next expected unblock time in case it referred to the task that is now in the Suspended state. */
		taskENTER_CRITICAL();
		{
			prvResetNextTaskUnblockTime();
		}
		taskEXIT_CRITICAL();
	}

	if (pxTCB == pxCurrentTCB)
	{
		if (xSchedulerRunning != pdFALSE)
		{
			// 当前任务已经被挂起了
			configASSERT(uxSchedulerSuspended == 0);
			portYIELD_WITHIN_API();
		}
		else
		{
			/* The scheduler is not running, but the task that was pointed
				to by pxCurrentTCB has just been suspended and pxCurrentTCB
				must be adjusted to point to a different task. */
			if (listCURRENT_LIST_LENGTH(&xSuspendedTaskList) == uxCurrentNumberOfTasks)
			{
				/* No other tasks are ready, so set pxCurrentTCB back to
					NULL so when the next task is created pxCurrentTCB will
					be set to point to it no matter what its relative priority
					is. */
				pxCurrentTCB = NULL;
			}
			else
			{
				vTaskSwitchContext();
			}
		}
	}
}

#endif /* INCLUDE_vTaskSuspend */

// 
#if (INCLUDE_vTaskSuspend == 1)
static BaseType_t prvTaskIsTaskSuspended(const TaskHandle_t xTask)
{
	BaseType_t xReturn = pdFALSE;
	const TCB_t *const pxTCB = (TCB_t *)xTask;

	// 对于正在调用的任务，检查挂起状态是没有意义的
	configASSERT(xTask);

	/* Is the task being resumed actually in the suspended list? */
	if (listIS_CONTAINED_WITHIN(&xSuspendedTaskList, &(pxTCB->xStateListItem)) != pdFALSE)
	{
		/* Has the task already been resumed from within an ISR? */
		if (listIS_CONTAINED_WITHIN(&xPendingReadyList, &(pxTCB->xEventListItem)) == pdFALSE)
		{
			/* Is it in the suspended list because it is in the	Suspended state, or because is is blocked with no timeout? */
			if (listIS_CONTAINED_WITHIN(NULL, &(pxTCB->xEventListItem)) != pdFALSE)
			{
				xReturn = pdTRUE;
			}
		}
	}

	return xReturn;
}
#endif

#if (INCLUDE_vTaskSuspend == 1)

void vTaskResume(TaskHandle_t xTaskToResume)
{
	TCB_t *const pxTCB = (TCB_t *)xTaskToResume;

	// 对于正在调用的任务，恢复是没有意义的
	configASSERT(xTaskToResume);

	// 
	if ((pxTCB != NULL) && (pxTCB != pxCurrentTCB))
	{
		taskENTER_CRITICAL();
		{
			if (prvTaskIsTaskSuspended(pxTCB) != pdFALSE)
			{
				traceTASK_RESUME(pxTCB);

				/* As we are in a critical section we can access the ready
					lists even if the scheduler is suspended. */
				(void)uxListRemove(&(pxTCB->xStateListItem));
				prvAddTaskToReadyList(pxTCB);

				/* We may have just resumed a higher priority task. */
				if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
				{
					/* This yield may not cause the task just resumed to run,
						but will leave the lists in the correct state for the
						next yield. */
					taskYIELD_IF_USING_PREEMPTION();
				}
			}
		}
		taskEXIT_CRITICAL();
	}
}

#endif /* INCLUDE_vTaskSuspend */

// 
#if ((INCLUDE_xTaskResumeFromISR == 1) && (INCLUDE_vTaskSuspend == 1))
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
{
	BaseType_t xYieldRequired = pdFALSE;
	TCB_t *const pxTCB = (TCB_t *)xTaskToResume;
	UBaseType_t uxSavedInterruptStatus;

	configASSERT(xTaskToResume);

	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		if (prvTaskIsTaskSuspended(pxTCB) != pdFALSE)
		{
			traceTASK_RESUME_FROM_ISR(pxTCB);

			/* Check the ready lists can be accessed. */
			if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
			{
				/* Ready lists can be accessed so move the task from the
					suspended list to the ready list directly. */
				if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
				{
					xYieldRequired = pdTRUE;
				}

				(void)uxListRemove(&(pxTCB->xStateListItem));
				prvAddTaskToReadyList(pxTCB);
			}
			else
			{
				/* The delayed or ready lists cannot be accessed so the task
					is held in the pending ready list until the scheduler is
					unsuspended. */
				vListInsertEnd(&(xPendingReadyList), &(pxTCB->xEventListItem));
			}
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

	return xYieldRequired;
}

#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */

// 开始调度
void vTaskStartScheduler(void)
{
	BaseType_t xReturn;

	// 将空闲任务添加到最低优先级
	#if (configSUPPORT_STATIC_ALLOCATION == 1)
	{
		StaticTask_t *pxIdleTaskTCBBuffer = NULL;
		StackType_t *pxIdleTaskStackBuffer = NULL;
		uint32_t ulIdleTaskStackSize;

		/* The Idle task is created using user provided RAM - obtain the
		address of the RAM then create the idle task. */
		vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize);
		xIdleTaskHandle = xTaskCreateStatic(prvIdleTask,
											"IDLE",
											ulIdleTaskStackSize,
											(void *)NULL,
											(tskIDLE_PRIORITY | portPRIVILEGE_BIT),
											pxIdleTaskStackBuffer,
											pxIdleTaskTCBBuffer); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

		if (xIdleTaskHandle != NULL)
		{
			xReturn = pdPASS;
		}
		else
		{
			xReturn = pdFAIL;
		}
	}
	#else
	{
		xReturn = xTaskCreate(prvIdleTask,
							  "IDLE", configMINIMAL_STACK_SIZE,
							  (void *)NULL,
							  (tskIDLE_PRIORITY | portPRIVILEGE_BIT),
							  &xIdleTaskHandle);
	}
	#endif /* configSUPPORT_STATIC_ALLOCATION */

	// 创建定时器
	#if (configUSE_TIMERS == 1)
	{
		if (xReturn == pdPASS)
		{
			xReturn = xTimerCreateTimerTask();
		}
	}
	#endif

	if (xReturn == pdPASS)
	{
		// 
		portDISABLE_INTERRUPTS();

		#if (configUSE_NEWLIB_REENTRANT == 1)
		{
			_impure_ptr = &(pxCurrentTCB->xNewLib_reent);
		}
		#endif

		xNextTaskUnblockTime = portMAX_DELAY;
		xSchedulerRunning = pdTRUE;
		xTickCount = (TickType_t)0U;

		//
		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

		// 设置计时器滴答是特定于硬件的，所以是port方法
		if (xPortStartScheduler() != pdFALSE)
		{
			// 不应该运行到这里
		}
		else
		{
			// 只有任务调用了xTaskEndScheduler()，才会运行到这里
		}
	}
	else
	{
		// 只有在没有足够的堆空间时，运行到这里才是正常的
		configASSERT(xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY);
	}

	// 阻止编译器警告
	(void)xIdleTaskHandle;
}
/*-----------------------------------------------------------*/

void vTaskEndScheduler(void)
{
	portDISABLE_INTERRUPTS();
	xSchedulerRunning = pdFALSE;
	vPortEndScheduler();
}
/*----------------------------------------------------------*/

void vTaskSuspendAll(void)
{
	/* A critical section is not required as the variable is of type
	BaseType_t.  Please read Richard Barry's reply in the following link to a
	post in the FreeRTOS support forum before reporting this as a bug! -
	http://goo.gl/wu4acr */
	++uxSchedulerSuspended;
}
/*----------------------------------------------------------*/

#if (configUSE_TICKLESS_IDLE != 0)

static TickType_t prvGetExpectedIdleTime(void)
{
	TickType_t xReturn;
	UBaseType_t uxHigherPriorityReadyTasks = pdFALSE;

/* uxHigherPriorityReadyTasks takes care of the case where
		configUSE_PREEMPTION is 0, so there may be tasks above the idle priority
		task that are in the Ready state, even though the idle task is
		running. */
#if (configUSE_PORT_OPTIMISED_TASK_SELECTION == 0)
	{
		if (uxTopReadyPriority > tskIDLE_PRIORITY)
		{
			uxHigherPriorityReadyTasks = pdTRUE;
		}
	}
#else
	{
		const UBaseType_t uxLeastSignificantBit = (UBaseType_t)0x01;

		/* When port optimised task selection is used the uxTopReadyPriority
			variable is used as a bit map.  If bits other than the least
			significant bit are set then there are tasks that have a priority
			above the idle priority that are in the Ready state.  This takes
			care of the case where the co-operative scheduler is in use. */
		if (uxTopReadyPriority > uxLeastSignificantBit)
		{
			uxHigherPriorityReadyTasks = pdTRUE;
		}
	}
#endif

	if (pxCurrentTCB->uxPriority > tskIDLE_PRIORITY)
	{
		xReturn = 0;
	}
	else if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[tskIDLE_PRIORITY])) > 1)
	{
		/* There are other idle priority tasks in the ready state.  If
			time slicing is used then the very next tick interrupt must be
			processed. */
		xReturn = 0;
	}
	else if (uxHigherPriorityReadyTasks != pdFALSE)
	{
		/* There are tasks in the Ready state that have a priority above the
			idle priority.  This path can only be reached if
			configUSE_PREEMPTION is 0. */
		xReturn = 0;
	}
	else
	{
		xReturn = xNextTaskUnblockTime - xTickCount;
	}

	return xReturn;
}

#endif /* configUSE_TICKLESS_IDLE */
/*----------------------------------------------------------*/

BaseType_t xTaskResumeAll(void)
{
	TCB_t *pxTCB = NULL;
	BaseType_t xAlreadyYielded = pdFALSE;

	/* If uxSchedulerSuspended is zero then this function does not match a
	previous call to vTaskSuspendAll(). */
	configASSERT(uxSchedulerSuspended);

	/* It is possible that an ISR caused a task to be removed from an event
	list while the scheduler was suspended.  If this was the case then the
	removed task will have been added to the xPendingReadyList.  Once the
	scheduler has been resumed it is safe to move all the pending ready
	tasks from this list into their appropriate ready list. */
	taskENTER_CRITICAL();
	{
		--uxSchedulerSuspended;

		if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
		{
			if (uxCurrentNumberOfTasks > (UBaseType_t)0U)
			{
				/* Move any readied tasks from the pending list into the
				appropriate ready list. */
				while (listLIST_IS_EMPTY(&xPendingReadyList) == pdFALSE)
				{
					pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY((&xPendingReadyList));
					(void)uxListRemove(&(pxTCB->xEventListItem));
					(void)uxListRemove(&(pxTCB->xStateListItem));
					prvAddTaskToReadyList(pxTCB);

					/* If the moved task has a priority higher than the current
					task then a yield must be performed. */
					if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
					{
						xYieldPending = pdTRUE;
					}
				}

				if (pxTCB != NULL)
				{
					/* A task was unblocked while the scheduler was suspended,
					which may have prevented the next unblock time from being
					re-calculated, in which case re-calculate it now.  Mainly
					important for low power tickless implementations, where
					this can prevent an unnecessary exit from low power
					state. */
					prvResetNextTaskUnblockTime();
				}

				/* If any ticks occurred while the scheduler was suspended then
				they should be processed now.  This ensures the tick count does
				not	slip, and that any delayed tasks are resumed at the correct
				time. */
				{
					UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */

					if (uxPendedCounts > (UBaseType_t)0U)
					{
						do
						{
							if (xTaskIncrementTick() != pdFALSE)
							{
								xYieldPending = pdTRUE;
							}
							--uxPendedCounts;
						} while (uxPendedCounts > (UBaseType_t)0U);

						uxPendedTicks = 0;
					}
				}

				if (xYieldPending != pdFALSE)
				{
#if (configUSE_PREEMPTION != 0)
					{
						xAlreadyYielded = pdTRUE;
					}
#endif
					taskYIELD_IF_USING_PREEMPTION();
				}
			}
		}
	}
	taskEXIT_CRITICAL();

	return xAlreadyYielded;
}
/*-----------------------------------------------------------*/

TickType_t xTaskGetTickCount(void)
{
	TickType_t xTicks;

	portTICK_TYPE_ENTER_CRITICAL();
	{
		xTicks = xTickCount;
	}
	portTICK_TYPE_EXIT_CRITICAL();

	return xTicks;
}
/*-----------------------------------------------------------*/

TickType_t xTaskGetTickCountFromISR(void)
{
	TickType_t xReturn;
	UBaseType_t uxSavedInterruptStatus;

	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR();
	{
		xReturn = xTickCount;
	}
	portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

	return xReturn;
}

// 获取任务数量
UBaseType_t uxTaskGetNumberOfTasks(void)
{
	return uxCurrentNumberOfTasks;
}

// 获取任务名称
char *pcTaskGetName(TaskHandle_t xTaskToQuery)
{
	TCB_t *pxTCB;

	pxTCB = prvGetTCBFromHandle(xTaskToQuery);
	configASSERT(pxTCB);
	return &(pxTCB->pcTaskName[0]);
}

// 
#if (INCLUDE_xTaskGetHandle == 1)

static TCB_t *prvSearchForNameWithinSingleList(List_t *pxList, const char pcNameToQuery[])
{
	TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL;
	UBaseType_t x;
	char cNextChar;

	/* This function is called with the scheduler suspended. */

	if (listCURRENT_LIST_LENGTH(pxList) > (UBaseType_t)0)
	{
		listGET_OWNER_OF_NEXT_ENTRY(pxFirstTCB, pxList);

		do
		{
			listGET_OWNER_OF_NEXT_ENTRY(pxNextTCB, pxList);

			/* Check each character in the name looking for a match or
				mismatch. */
			for (x = (UBaseType_t)0; x < (UBaseType_t)configMAX_TASK_NAME_LEN; x++)
			{
				cNextChar = pxNextTCB->pcTaskName[x];

				if (cNextChar != pcNameToQuery[x])
				{
					/* Characters didn't match. */
					break;
				}
				else if (cNextChar == 0x00)
				{
					/* Both strings terminated, a match must have been
						found. */
					pxReturn = pxNextTCB;
					break;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}

			if (pxReturn != NULL)
			{
				/* The handle has been found. */
				break;
			}

		} while (pxNextTCB != pxFirstTCB);
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	return pxReturn;
}

#endif /* INCLUDE_xTaskGetHandle */
/*-----------------------------------------------------------*/

#if (INCLUDE_xTaskGetHandle == 1)

TaskHandle_t xTaskGetHandle(const char *pcNameToQuery) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
	UBaseType_t uxQueue = configMAX_PRIORITIES;
	TCB_t *pxTCB;

	/* Task names will be truncated to configMAX_TASK_NAME_LEN - 1 bytes. */
	configASSERT(strlen(pcNameToQuery) < configMAX_TASK_NAME_LEN);

	vTaskSuspendAll();
	{
		/* Search the ready lists. */
		do
		{
			uxQueue--;
			pxTCB = prvSearchForNameWithinSingleList((List_t *)&(pxReadyTasksLists[uxQueue]), pcNameToQuery);

			if (pxTCB != NULL)
			{
				/* Found the handle. */
				break;
			}

		} while (uxQueue > (UBaseType_t)tskIDLE_PRIORITY); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

		/* Search the delayed lists. */
		if (pxTCB == NULL)
		{
			pxTCB = prvSearchForNameWithinSingleList((List_t *)pxDelayedTaskList, pcNameToQuery);
		}

		if (pxTCB == NULL)
		{
			pxTCB = prvSearchForNameWithinSingleList((List_t *)pxOverflowDelayedTaskList, pcNameToQuery);
		}

#if (INCLUDE_vTaskSuspend == 1)
		{
			if (pxTCB == NULL)
			{
				/* Search the suspended list. */
				pxTCB = prvSearchForNameWithinSingleList(&xSuspendedTaskList, pcNameToQuery);
			}
		}
#endif

#if (INCLUDE_vTaskDelete == 1)
		{
			if (pxTCB == NULL)
			{
				/* Search the deleted list. */
				pxTCB = prvSearchForNameWithinSingleList(&xTasksWaitingTermination, pcNameToQuery);
			}
		}
#endif
	}
	(void)xTaskResumeAll();

	return (TaskHandle_t)pxTCB;
}

#endif /* INCLUDE_xTaskGetHandle */
/*-----------------------------------------------------------*/

#if (configUSE_TRACE_FACILITY == 1)

UBaseType_t uxTaskGetSystemState(TaskStatus_t *const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t *const pulTotalRunTime)
{
	UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES;

	vTaskSuspendAll();
	{
		/* Is there a space in the array for each task in the system? */
		if (uxArraySize >= uxCurrentNumberOfTasks)
		{
			/* Fill in an TaskStatus_t structure with information on each task in the Ready state. */
			do
			{
				uxQueue--;
				uxTask += prvListTasksWithinSingleList(&(pxTaskStatusArray[uxTask]), &(pxReadyTasksLists[uxQueue]), eReady);

			} while (uxQueue > (UBaseType_t)tskIDLE_PRIORITY); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

			/* Fill in an TaskStatus_t structure with information on each
				task in the Blocked state. */
			uxTask += prvListTasksWithinSingleList(&(pxTaskStatusArray[uxTask]), (List_t *)pxDelayedTaskList, eBlocked);
			uxTask += prvListTasksWithinSingleList(&(pxTaskStatusArray[uxTask]), (List_t *)pxOverflowDelayedTaskList, eBlocked);

#if (INCLUDE_vTaskDelete == 1)
			{
				/* Fill in an TaskStatus_t structure with information on
					each task that has been deleted but not yet cleaned up. */
				uxTask += prvListTasksWithinSingleList(&(pxTaskStatusArray[uxTask]), &xTasksWaitingTermination, eDeleted);
			}
#endif

#if (INCLUDE_vTaskSuspend == 1)
			{
				/* Fill in an TaskStatus_t structure with information on
					each task in the Suspended state. */
				uxTask += prvListTasksWithinSingleList(&(pxTaskStatusArray[uxTask]), &xSuspendedTaskList, eSuspended);
			}
#endif

#if (configGENERATE_RUN_TIME_STATS == 1)
			{
				if (pulTotalRunTime != NULL)
				{
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
					portALT_GET_RUN_TIME_COUNTER_VALUE((*pulTotalRunTime));
#else
					*pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
#endif
				}
			}
#else
			{
				if (pulTotalRunTime != NULL)
				{
					*pulTotalRunTime = 0;
				}
			}
#endif
		}
	}
	(void)xTaskResumeAll();

	return uxTask;
}

#endif /* configUSE_TRACE_FACILITY */
/*----------------------------------------------------------*/

#if (INCLUDE_xTaskGetIdleTaskHandle == 1)
TaskHandle_t xTaskGetIdleTaskHandle(void)
{
	configASSERT((xIdleTaskHandle != NULL));
	return xIdleTaskHandle;
}
#endif

/* This conditional compilation should use inequality to 0, not equality to 1.
This is to ensure vTaskStepTick() is available when user defined low power mode
implementations require configUSE_TICKLESS_IDLE to be set to a value other than
1. */
#if (configUSE_TICKLESS_IDLE != 0)

void vTaskStepTick(const TickType_t xTicksToJump)
{
	/* Correct the tick count value after a period during which the tick
		was suppressed.  Note this does *not* call the tick hook function for
		each stepped tick. */
	configASSERT((xTickCount + xTicksToJump) <= xNextTaskUnblockTime);
	xTickCount += xTicksToJump;
	traceINCREASE_TICK_COUNT(xTicksToJump);
}

#endif /* configUSE_TICKLESS_IDLE */
/*----------------------------------------------------------*/

#if (INCLUDE_xTaskAbortDelay == 1)

BaseType_t xTaskAbortDelay(TaskHandle_t xTask)
{
	TCB_t *pxTCB = (TCB_t *)xTask;
	BaseType_t xReturn = pdFALSE;

	configASSERT(pxTCB);

	vTaskSuspendAll();
	{
		/* A task can only be prematurely removed from the Blocked state if
			it is actually in the Blocked state. */
		if (eTaskGetState(xTask) == eBlocked)
		{
			/* Remove the reference to the task from the blocked list.  An
				interrupt won't touch the xStateListItem because the
				scheduler is suspended. */
			(void)uxListRemove(&(pxTCB->xStateListItem));

			/* Is the task waiting on an event also?  If so remove it from
				the event list too.  Interrupts can touch the event list item,
				even though the scheduler is suspended, so a critical section
				is used. */
			taskENTER_CRITICAL();
			{
				if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL)
				{
					(void)uxListRemove(&(pxTCB->xEventListItem));
					pxTCB->ucDelayAborted = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();

			/* Place the unblocked task into the appropriate ready list. */
			prvAddTaskToReadyList(pxTCB);

/* A task being unblocked cannot cause an immediate context
				switch if preemption is turned off. */
#if (configUSE_PREEMPTION == 1)
			{
				/* Preemption is on, but a context switch should only be
					performed if the unblocked task has a priority that is
					equal to or higher than the currently executing task. */
				if (pxTCB->uxPriority > pxCurrentTCB->uxPriority)
				{
					/* Pend the yield to be performed when the scheduler
						is unsuspended. */
					xYieldPending = pdTRUE;
				}
			}
#endif /* configUSE_PREEMPTION */
		}
	}
	xTaskResumeAll();

	return xReturn;
}

#endif /* INCLUDE_xTaskAbortDelay */
/*----------------------------------------------------------*/

BaseType_t xTaskIncrementTick(void)
{
	TCB_t *pxTCB;
	TickType_t xItemValue;
	BaseType_t xSwitchRequired = pdFALSE;

	// 滴答定时中断时由port层调用，增加tick值然后看是否解锁任务
	traceTASK_INCREMENT_TICK(xTickCount);
	if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
	{
		const TickType_t xConstTickCount = xTickCount + 1;
		xTickCount = xConstTickCount;

		// 切换延时列表
		if (xConstTickCount == (TickType_t)0U)
		{
			taskSWITCH_DELAYED_LISTS();
		}

		// 任务是按照唤醒时间排序的，找最近的唤醒时间即可
		if (xConstTickCount >= xNextTaskUnblockTime)
		{
			for (;;)
			{
				if (listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE)
				{
					xNextTaskUnblockTime = portMAX_DELAY;
					break;
				}
				else
				{
					// 
					pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);
					xItemValue = listGET_LIST_ITEM_VALUE(&(pxTCB->xStateListItem));

					if (xConstTickCount < xItemValue)
					{
						// 现在还不是解锁的时候
						xNextTaskUnblockTime = xItemValue;
						break;
					}

					// 从延时列表中移除
					(void)uxListRemove(&(pxTCB->xStateListItem));

					// 任务也在事件列表中，从事件列表中移除
					if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL)
					{
						(void)uxListRemove(&(pxTCB->xEventListItem));
					}

					/* Place the unblocked task into the appropriate ready list. */
					prvAddTaskToReadyList(pxTCB);

					/* A task being unblocked cannot cause an immediate context switch if preemption is turned off. */
					#if (configUSE_PREEMPTION == 1)
					{
						/* Preemption is on, but a context switch should
						only be performed if the unblocked task has a
						priority that is equal to or higher than the
						currently executing task. */
						if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
						{
							xSwitchRequired = pdTRUE;
						}
					}
					#endif /* configUSE_PREEMPTION */
				}
			}
		}

/* Tasks of equal priority to the currently running task will share
		processing time (time slice) if preemption is on, and the application
		writer has not explicitly turned time slicing off. */
#if ((configUSE_PREEMPTION == 1) && (configUSE_TIME_SLICING == 1))
		{
			if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[pxCurrentTCB->uxPriority])) > (UBaseType_t)1)
			{
				xSwitchRequired = pdTRUE;
			}
		}
#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

#if (configUSE_TICK_HOOK == 1)
		{
			/* Guard against the tick hook being called when the pended tick
			count is being unwound (when the scheduler is being unlocked). */
			if (uxPendedTicks == (UBaseType_t)0U)
			{
				vApplicationTickHook();
			}
		}
#endif /* configUSE_TICK_HOOK */
	}
	else
	{
		++uxPendedTicks;

/* The tick hook gets called at regular intervals, even if the
		scheduler is locked. */
#if (configUSE_TICK_HOOK == 1)
		{
			vApplicationTickHook();
		}
#endif
	}

#if (configUSE_PREEMPTION == 1)
	{
		if (xYieldPending != pdFALSE)
		{
			xSwitchRequired = pdTRUE;
		}
	}
#endif /* configUSE_PREEMPTION */

	return xSwitchRequired;
}
/*-----------------------------------------------------------*/

#if (configUSE_APPLICATION_TASK_TAG == 1)

void vTaskSetApplicationTaskTag(TaskHandle_t xTask, TaskHookFunction_t pxHookFunction)
{
	TCB_t *xTCB;

	/* If xTask is NULL then it is the task hook of the calling task that is
		getting set. */
	if (xTask == NULL)
	{
		xTCB = (TCB_t *)pxCurrentTCB;
	}
	else
	{
		xTCB = (TCB_t *)xTask;
	}

	/* Save the hook function in the TCB.  A critical section is required as
		the value can be accessed from an interrupt. */
	taskENTER_CRITICAL();
	xTCB->pxTaskTag = pxHookFunction;
	taskEXIT_CRITICAL();
}

#endif /* configUSE_APPLICATION_TASK_TAG */
/*-----------------------------------------------------------*/

#if (configUSE_APPLICATION_TASK_TAG == 1)

TaskHookFunction_t xTaskGetApplicationTaskTag(TaskHandle_t xTask)
{
	TCB_t *xTCB;
	TaskHookFunction_t xReturn;

	/* If xTask is NULL then we are setting our own task hook. */
	if (xTask == NULL)
	{
		xTCB = (TCB_t *)pxCurrentTCB;
	}
	else
	{
		xTCB = (TCB_t *)xTask;
	}

	/* Save the hook function in the TCB.  A critical section is required as
		the value can be accessed from an interrupt. */
	taskENTER_CRITICAL();
	{
		xReturn = xTCB->pxTaskTag;
	}
	taskEXIT_CRITICAL();

	return xReturn;
}

#endif /* configUSE_APPLICATION_TASK_TAG */
/*-----------------------------------------------------------*/

#if (configUSE_APPLICATION_TASK_TAG == 1)

BaseType_t xTaskCallApplicationTaskHook(TaskHandle_t xTask, void *pvParameter)
{
	TCB_t *xTCB;
	BaseType_t xReturn;

	/* If xTask is NULL then we are calling our own task hook. */
	if (xTask == NULL)
	{
		xTCB = (TCB_t *)pxCurrentTCB;
	}
	else
	{
		xTCB = (TCB_t *)xTask;
	}

	if (xTCB->pxTaskTag != NULL)
	{
		xReturn = xTCB->pxTaskTag(pvParameter);
	}
	else
	{
		xReturn = pdFAIL;
	}

	return xReturn;
}

#endif /* configUSE_APPLICATION_TASK_TAG */
/*-----------------------------------------------------------*/

// 切换上下文，PendSVHandler中断处理中有调用
void vTaskSwitchContext(void)
{
	if (uxSchedulerSuspended != (UBaseType_t)pdFALSE)
	{
		// 调度已经挂起了，不需要了
		xYieldPending = pdTRUE;
	}
	else
	{
		xYieldPending = pdFALSE;
		traceTASK_SWITCHED_OUT();

#if (configGENERATE_RUN_TIME_STATS == 1)
		{
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
			portALT_GET_RUN_TIME_COUNTER_VALUE(ulTotalRunTime);
#else
			ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
#endif

			/* Add the amount of time the task has been running to the
				accumulated time so far.  The time the task started running was
				stored in ulTaskSwitchedInTime.  Note that there is no overflow
				protection here so count values are only valid until the timer
				overflows.  The guard against negative values is to protect
				against suspect run time stat counter implementations - which
				are provided by the application, not the kernel. */
			if (ulTotalRunTime > ulTaskSwitchedInTime)
			{
				pxCurrentTCB->ulRunTimeCounter += (ulTotalRunTime - ulTaskSwitchedInTime);
			}
			ulTaskSwitchedInTime = ulTotalRunTime;
		}
#endif /* configGENERATE_RUN_TIME_STATS */

		// 如果定义了对应的方法，检查栈溢出
		taskCHECK_FOR_STACK_OVERFLOW();

		// 选择一个新的最高优先级任务，赋值给pxCurrentTCB
		taskSELECT_HIGHEST_PRIORITY_TASK();
		traceTASK_SWITCHED_IN();

		#if (configUSE_NEWLIB_REENTRANT == 1)
		{
			_impure_ptr = &(pxCurrentTCB->xNewLib_reent);
		}
		#endif /* configUSE_NEWLIB_REENTRANT */
	}
}
/*-----------------------------------------------------------*/

void vTaskPlaceOnEventList(List_t *const pxEventList, const TickType_t xTicksToWait)
{
	configASSERT(pxEventList);

	/* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE
	SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */

	/* Place the event list item of the TCB in the appropriate event list.
	This is placed in the list in priority order so the highest priority task
	is the first to be woken by the event.  The queue that contains the event
	list is locked, preventing simultaneous access from interrupts. */
	vListInsert(pxEventList, &(pxCurrentTCB->xEventListItem));

	prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
}
/*-----------------------------------------------------------*/

void vTaskPlaceOnUnorderedEventList(List_t *pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait)
{
	configASSERT(pxEventList);

	/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED.  It is used by
	the event groups implementation. */
	configASSERT(uxSchedulerSuspended != 0);

	/* Store the item value in the event list item.  It is safe to access the
	event list item here as interrupts won't access the event list item of a
	task that is not in the Blocked state. */
	listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xEventListItem), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE);

	/* Place the event list item of the TCB at the end of the appropriate event
	list.  It is safe to access the event list here because it is part of an
	event group implementation - and interrupts don't access event groups
	directly (instead they access them indirectly by pending function calls to
	the task level). */
	vListInsertEnd(pxEventList, &(pxCurrentTCB->xEventListItem));

	prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
}
/*-----------------------------------------------------------*/

#if (configUSE_TIMERS == 1)

void vTaskPlaceOnEventListRestricted(List_t *const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely)
{
	configASSERT(pxEventList);

	/* This function should not be called by application code hence the
		'Restricted' in its name.  It is not part of the public API.  It is
		designed for use by kernel code, and has special calling requirements -
		it should be called with the scheduler suspended. */

	/* Place the event list item of the TCB in the appropriate event list.
		In this case it is assume that this is the only task that is going to
		be waiting on this event list, so the faster vListInsertEnd() function
		can be used in place of vListInsert. */
	vListInsertEnd(pxEventList, &(pxCurrentTCB->xEventListItem));

	/* If the task should block indefinitely then set the block time to a
		value that will be recognised as an indefinite delay inside the
		prvAddCurrentTaskToDelayedList() function. */
	if (xWaitIndefinitely != pdFALSE)
	{
		xTicksToWait = portMAX_DELAY;
	}

	traceTASK_DELAY_UNTIL((xTickCount + xTicksToWait));
	prvAddCurrentTaskToDelayedList(xTicksToWait, xWaitIndefinitely);
}

#endif /* configUSE_TIMERS */
/*-----------------------------------------------------------*/

BaseType_t xTaskRemoveFromEventList(const List_t *const pxEventList)
{
	TCB_t *pxUnblockedTCB;
	BaseType_t xReturn;

	/* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION.  It can also be
	called from a critical section within an ISR. */

	/* The event list is sorted in priority order, so the first in the list can
	be removed as it is known to be the highest priority.  Remove the TCB from
	the delayed list, and add it to the ready list.

	If an event is for a queue that is locked then this function will never
	get called - the lock count on the queue will get modified instead.  This
	means exclusive access to the event list is guaranteed here.

	This function assumes that a check has already been made to ensure that
	pxEventList is not empty. */
	pxUnblockedTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxEventList);
	configASSERT(pxUnblockedTCB);
	(void)uxListRemove(&(pxUnblockedTCB->xEventListItem));

	if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
	{
		(void)uxListRemove(&(pxUnblockedTCB->xStateListItem));
		prvAddTaskToReadyList(pxUnblockedTCB);
	}
	else
	{
		/* The delayed and ready lists cannot be accessed, so hold this task
		pending until the scheduler is resumed. */
		vListInsertEnd(&(xPendingReadyList), &(pxUnblockedTCB->xEventListItem));
	}

	if (pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority)
	{
		/* Return true if the task removed from the event list has a higher
		priority than the calling task.  This allows the calling task to know if
		it should force a context switch now. */
		xReturn = pdTRUE;

		/* Mark that a yield is pending in case the user is not using the
		"xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */
		xYieldPending = pdTRUE;
	}
	else
	{
		xReturn = pdFALSE;
	}

#if (configUSE_TICKLESS_IDLE != 0)
	{
		/* If a task is blocked on a kernel object then xNextTaskUnblockTime
		might be set to the blocked task's time out time.  If the task is
		unblocked for a reason other than a timeout xNextTaskUnblockTime is
		normally left unchanged, because it is automatically reset to a new
		value when the tick count equals xNextTaskUnblockTime.  However if
		tickless idling is used it might be more important to enter sleep mode
		at the earliest possible time - so reset xNextTaskUnblockTime here to
		ensure it is updated at the earliest possible time. */
		prvResetNextTaskUnblockTime();
	}
#endif

	return xReturn;
}
/*-----------------------------------------------------------*/

BaseType_t xTaskRemoveFromUnorderedEventList(ListItem_t *pxEventListItem, const TickType_t xItemValue)
{
	TCB_t *pxUnblockedTCB;
	BaseType_t xReturn;

	/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED.  It is used by
	the event flags implementation. */
	configASSERT(uxSchedulerSuspended != pdFALSE);

	/* Store the new item value in the event list. */
	listSET_LIST_ITEM_VALUE(pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE);

	/* Remove the event list form the event flag.  Interrupts do not access
	event flags. */
	pxUnblockedTCB = (TCB_t *)listGET_LIST_ITEM_OWNER(pxEventListItem);
	configASSERT(pxUnblockedTCB);
	(void)uxListRemove(pxEventListItem);

	/* Remove the task from the delayed list and add it to the ready list.  The
	scheduler is suspended so interrupts will not be accessing the ready
	lists. */
	(void)uxListRemove(&(pxUnblockedTCB->xStateListItem));
	prvAddTaskToReadyList(pxUnblockedTCB);

	if (pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority)
	{
		/* Return true if the task removed from the event list has
		a higher priority than the calling task.  This allows
		the calling task to know if it should force a context
		switch now. */
		xReturn = pdTRUE;

		/* Mark that a yield is pending in case the user is not using the
		"xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */
		xYieldPending = pdTRUE;
	}
	else
	{
		xReturn = pdFALSE;
	}

	return xReturn;
}
/*-----------------------------------------------------------*/

// 设置超时状态 
void vTaskSetTimeOutState(TimeOut_t *const pxTimeOut)
{
	configASSERT(pxTimeOut);
	pxTimeOut->xOverflowCount = xNumOfOverflows;
	pxTimeOut->xTimeOnEntering = xTickCount;
}
/*-----------------------------------------------------------*/

BaseType_t xTaskCheckForTimeOut(TimeOut_t *const pxTimeOut, TickType_t *const pxTicksToWait)
{
	BaseType_t xReturn;

	configASSERT(pxTimeOut);
	configASSERT(pxTicksToWait);

	taskENTER_CRITICAL();
	{
		/* Minor optimisation.  The tick count cannot change in this block. */
		const TickType_t xConstTickCount = xTickCount;

#if (INCLUDE_xTaskAbortDelay == 1)
		if (pxCurrentTCB->ucDelayAborted != pdFALSE)
		{
			/* The delay was aborted, which is not the same as a time out,
				but has the same result. */
			pxCurrentTCB->ucDelayAborted = pdFALSE;
			xReturn = pdTRUE;
		}
		else
#endif

#if (INCLUDE_vTaskSuspend == 1)
			if (*pxTicksToWait == portMAX_DELAY)
		{
			/* If INCLUDE_vTaskSuspend is set to 1 and the block time
				specified is the maximum block time then the task should block
				indefinitely, and therefore never time out. */
			xReturn = pdFALSE;
		}
		else
#endif

			if ((xNumOfOverflows != pxTimeOut->xOverflowCount) && (xConstTickCount >= pxTimeOut->xTimeOnEntering)) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */
		{
			/* The tick count is greater than the time at which
			vTaskSetTimeout() was called, but has also overflowed since
			vTaskSetTimeOut() was called.  It must have wrapped all the way
			around and gone past again. This passed since vTaskSetTimeout()
			was called. */
			xReturn = pdTRUE;
		}
		else if (((TickType_t)(xConstTickCount - pxTimeOut->xTimeOnEntering)) < *pxTicksToWait) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
		{
			/* Not a genuine timeout. Adjust parameters for time remaining. */
			*pxTicksToWait -= (xConstTickCount - pxTimeOut->xTimeOnEntering);
			vTaskSetTimeOutState(pxTimeOut);
			xReturn = pdFALSE;
		}
		else
		{
			xReturn = pdTRUE;
		}
	}
	taskEXIT_CRITICAL();

	return xReturn;
}
/*-----------------------------------------------------------*/

void vTaskMissedYield(void)
{
	xYieldPending = pdTRUE;
}
/*-----------------------------------------------------------*/

#if (configUSE_TRACE_FACILITY == 1)

UBaseType_t uxTaskGetTaskNumber(TaskHandle_t xTask)
{
	UBaseType_t uxReturn;
	TCB_t *pxTCB;

	if (xTask != NULL)
	{
		pxTCB = (TCB_t *)xTask;
		uxReturn = pxTCB->uxTaskNumber;
	}
	else
	{
		uxReturn = 0U;
	}

	return uxReturn;
}

#endif /* configUSE_TRACE_FACILITY */
/*-----------------------------------------------------------*/

#if (configUSE_TRACE_FACILITY == 1)

void vTaskSetTaskNumber(TaskHandle_t xTask, const UBaseType_t uxHandle)
{
	TCB_t *pxTCB;

	if (xTask != NULL)
	{
		pxTCB = (TCB_t *)xTask;
		pxTCB->uxTaskNumber = uxHandle;
	}
}

#endif /* configUSE_TRACE_FACILITY */

/*
 * -----------------------------------------------------------
 * The Idle task.
 * ----------------------------------------------------------
 *
 * The portTASK_FUNCTION() macro is used to allow port/compiler specific
 * language extensions.  The equivalent prototype for this function is:
 *
 * void prvIdleTask( void *pvParameters );
 *
 */
static portTASK_FUNCTION(prvIdleTask, pvParameters)
{
	/* Stop warnings. */
	(void)pvParameters;

	/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE
	SCHEDULER IS STARTED. **/

	for (;;)
	{
		/* See if any tasks have deleted themselves - if so then the idle task
		is responsible for freeing the deleted task's TCB and stack. */
		prvCheckTasksWaitingTermination();

#if (configUSE_PREEMPTION == 0)
		{
			/* If we are not using preemption we keep forcing a task switch to
			see if any other task has become available.  If we are using
			preemption we don't need to do this as any task becoming available
			will automatically get the processor anyway. */
			taskYIELD();
		}
#endif /* configUSE_PREEMPTION */

#if ((configUSE_PREEMPTION == 1) && (configIDLE_SHOULD_YIELD == 1))
		{
			/* When using preemption tasks of equal priority will be
			timesliced.  If a task that is sharing the idle priority is ready
			to run then the idle task should yield before the end of the
			timeslice.

			A critical region is not required here as we are just reading from
			the list, and an occasional incorrect value will not matter.  If
			the ready list at the idle priority contains more than one task
			then a task other than the idle task is ready to execute. */
			if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[tskIDLE_PRIORITY])) > (UBaseType_t)1)
			{
				taskYIELD();
			}
		}
#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */

#if (configUSE_IDLE_HOOK == 1)
		{
			extern void vApplicationIdleHook(void);

			/* Call the user defined function from within the idle task.  This
			allows the application designer to add background functionality
			without the overhead of a separate task.
			NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
			CALL A FUNCTION THAT MIGHT BLOCK. */
			vApplicationIdleHook();
		}
#endif /* configUSE_IDLE_HOOK */

/* This conditional compilation should use inequality to 0, not equality
		to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
		user defined low power mode	implementations require
		configUSE_TICKLESS_IDLE to be set to a value other than 1. */
#if (configUSE_TICKLESS_IDLE != 0)
		{
			TickType_t xExpectedIdleTime;

			/* It is not desirable to suspend then resume the scheduler on
			each iteration of the idle task.  Therefore, a preliminary
			test of the expected idle time is performed without the
			scheduler suspended.  The result here is not necessarily
			valid. */
			xExpectedIdleTime = prvGetExpectedIdleTime();

			if (xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP)
			{
				vTaskSuspendAll();
				{
					/* Now the scheduler is suspended, the expected idle
					time can be sampled again, and this time its value can
					be used. */
					configASSERT(xNextTaskUnblockTime >= xTickCount);
					xExpectedIdleTime = prvGetExpectedIdleTime();

					if (xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP)
					{
						traceLOW_POWER_IDLE_BEGIN();
						portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);
						traceLOW_POWER_IDLE_END();
					}
				}
				(void)xTaskResumeAll();
			}
		}
#endif /* configUSE_TICKLESS_IDLE */
	}
}
/*-----------------------------------------------------------*/

#if (configUSE_TICKLESS_IDLE != 0)

eSleepModeStatus eTaskConfirmSleepModeStatus(void)
{
	/* The idle task exists in addition to the application tasks. */
	const UBaseType_t uxNonApplicationTasks = 1;
	eSleepModeStatus eReturn = eStandardSleep;

	if (listCURRENT_LIST_LENGTH(&xPendingReadyList) != 0)
	{
		/* A task was made ready while the scheduler was suspended. */
		eReturn = eAbortSleep;
	}
	else if (xYieldPending != pdFALSE)
	{
		/* A yield was pended while the scheduler was suspended. */
		eReturn = eAbortSleep;
	}
	else
	{
		/* If all the tasks are in the suspended list (which might mean they
			have an infinite block time rather than actually being suspended)
			then it is safe to turn all clocks off and just wait for external
			interrupts. */
		if (listCURRENT_LIST_LENGTH(&xSuspendedTaskList) == (uxCurrentNumberOfTasks - uxNonApplicationTasks))
		{
			eReturn = eNoTasksWaitingTimeout;
		}
	}

	return eReturn;
}

#endif /* configUSE_TICKLESS_IDLE */
/*-----------------------------------------------------------*/

#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0)

void vTaskSetThreadLocalStoragePointer(TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue)
{
	TCB_t *pxTCB;

	if (xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS)
	{
		pxTCB = prvGetTCBFromHandle(xTaskToSet);
		pxTCB->pvThreadLocalStoragePointers[xIndex] = pvValue;
	}
}

#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */
/*-----------------------------------------------------------*/

#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0)

void *pvTaskGetThreadLocalStoragePointer(TaskHandle_t xTaskToQuery, BaseType_t xIndex)
{
	void *pvReturn = NULL;
	TCB_t *pxTCB;

	if (xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS)
	{
		pxTCB = prvGetTCBFromHandle(xTaskToQuery);
		pvReturn = pxTCB->pvThreadLocalStoragePointers[xIndex];
	}
	else
	{
		pvReturn = NULL;
	}

	return pvReturn;
}

#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */
/*-----------------------------------------------------------*/

#if (portUSING_MPU_WRAPPERS == 1)

void vTaskAllocateMPURegions(TaskHandle_t xTaskToModify, const MemoryRegion_t *const xRegions)
{
	TCB_t *pxTCB;

	/* If null is passed in here then we are modifying the MPU settings of
		the calling task. */
	pxTCB = prvGetTCBFromHandle(xTaskToModify);

	vPortStoreTaskMPUSettings(&(pxTCB->xMPUSettings), xRegions, NULL, 0);
}

#endif /* portUSING_MPU_WRAPPERS */
/*-----------------------------------------------------------*/

static void prvInitialiseTaskLists(void)
{
	UBaseType_t uxPriority;

	for (uxPriority = (UBaseType_t)0U; uxPriority < (UBaseType_t)configMAX_PRIORITIES; uxPriority++)
	{
		vListInitialise(&(pxReadyTasksLists[uxPriority]));
	}

	vListInitialise(&xDelayedTaskList1);
	vListInitialise(&xDelayedTaskList2);
	vListInitialise(&xPendingReadyList);

#if (INCLUDE_vTaskDelete == 1)
	{
		vListInitialise(&xTasksWaitingTermination);
	}
#endif /* INCLUDE_vTaskDelete */

	#if (INCLUDE_vTaskSuspend == 1)
	{
		vListInitialise(&xSuspendedTaskList);
	}
	#endif /* INCLUDE_vTaskSuspend */

	/* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList using list2. */
	pxDelayedTaskList = &xDelayedTaskList1;
	pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
/*-----------------------------------------------------------*/

static void prvCheckTasksWaitingTermination(void)
{

	/** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/

#if (INCLUDE_vTaskDelete == 1)
	{
		BaseType_t xListIsEmpty;

		/* ucTasksDeleted is used to prevent vTaskSuspendAll() being called too often in the idle task. */
		while (uxDeletedTasksWaitingCleanUp > (UBaseType_t)0U)
		{
			vTaskSuspendAll();
			{
				xListIsEmpty = listLIST_IS_EMPTY(&xTasksWaitingTermination);
			}
			(void)xTaskResumeAll();

			if (xListIsEmpty == pdFALSE)
			{
				TCB_t *pxTCB;

				taskENTER_CRITICAL();
				{
					pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY((&xTasksWaitingTermination));
					(void)uxListRemove(&(pxTCB->xStateListItem));
					--uxCurrentNumberOfTasks;
					--uxDeletedTasksWaitingCleanUp;
				}
				taskEXIT_CRITICAL();

				prvDeleteTCB(pxTCB);
			}
		}
	}
#endif /* INCLUDE_vTaskDelete */
}
/*-----------------------------------------------------------*/

#if (configUSE_TRACE_FACILITY == 1)

void vTaskGetInfo(TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState)
{
	TCB_t *pxTCB;

	/* xTask is NULL then get the state of the calling task. */
	pxTCB = prvGetTCBFromHandle(xTask);

	pxTaskStatus->xHandle = (TaskHandle_t)pxTCB;
	pxTaskStatus->pcTaskName = (const char *)&(pxTCB->pcTaskName[0]);
	pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority;
	pxTaskStatus->pxStackBase = pxTCB->pxStack;
	pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber;

#if (INCLUDE_vTaskSuspend == 1)
	{
		/* If the task is in the suspended list then there is a chance it is
			actually just blocked indefinitely - so really it should be reported as
			being in the Blocked state. */
		if (pxTaskStatus->eCurrentState == eSuspended)
		{
			vTaskSuspendAll();
			{
				if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL)
				{
					pxTaskStatus->eCurrentState = eBlocked;
				}
			}
			xTaskResumeAll();
		}
	}
#endif /* INCLUDE_vTaskSuspend */

#if (configUSE_MUTEXES == 1)
	{
		pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority;
	}
#else
	{
		pxTaskStatus->uxBasePriority = 0;
	}
#endif

#if (configGENERATE_RUN_TIME_STATS == 1)
	{
		pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter;
	}
#else
	{
		pxTaskStatus->ulRunTimeCounter = 0;
	}
#endif

	/* Obtaining the task state is a little fiddly, so is only done if the value
		of eState passed into this function is eInvalid - otherwise the state is
		just set to whatever is passed in. */
	if (eState != eInvalid)
	{
		pxTaskStatus->eCurrentState = eState;
	}
	else
	{
		pxTaskStatus->eCurrentState = eTaskGetState(xTask);
	}

	/* Obtaining the stack space takes some time, so the xGetFreeStackSpace
		parameter is provided to allow it to be skipped. */
	if (xGetFreeStackSpace != pdFALSE)
	{
#if (portSTACK_GROWTH > 0)
		{
			pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace((uint8_t *)pxTCB->pxEndOfStack);
		}
#else
		{
			pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace((uint8_t *)pxTCB->pxStack);
		}
#endif
	}
	else
	{
		pxTaskStatus->usStackHighWaterMark = 0;
	}
}

#endif /* configUSE_TRACE_FACILITY */
/*-----------------------------------------------------------*/

#if (configUSE_TRACE_FACILITY == 1)

static UBaseType_t prvListTasksWithinSingleList(TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState)
{
	volatile TCB_t *pxNextTCB, *pxFirstTCB;
	UBaseType_t uxTask = 0;

	if (listCURRENT_LIST_LENGTH(pxList) > (UBaseType_t)0)
	{
		listGET_OWNER_OF_NEXT_ENTRY(pxFirstTCB, pxList);

		/* Populate an TaskStatus_t structure within the
			pxTaskStatusArray array for each task that is referenced from
			pxList.  See the definition of TaskStatus_t in task.h for the
			meaning of each TaskStatus_t structure member. */
		do
		{
			listGET_OWNER_OF_NEXT_ENTRY(pxNextTCB, pxList);
			vTaskGetInfo((TaskHandle_t)pxNextTCB, &(pxTaskStatusArray[uxTask]), pdTRUE, eState);
			uxTask++;
		} while (pxNextTCB != pxFirstTCB);
	}

	return uxTask;
}

#endif /* configUSE_TRACE_FACILITY */
/*-----------------------------------------------------------*/

#if ((configUSE_TRACE_FACILITY == 1) || (INCLUDE_uxTaskGetStackHighWaterMark == 1))

static uint16_t prvTaskCheckFreeStackSpace(const uint8_t *pucStackByte)
{
	uint32_t ulCount = 0U;

	while (*pucStackByte == (uint8_t)tskSTACK_FILL_BYTE)
	{
		pucStackByte -= portSTACK_GROWTH;
		ulCount++;
	}

	ulCount /= (uint32_t)sizeof(StackType_t);

	return (uint16_t)ulCount;
}

#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) */
/*-----------------------------------------------------------*/

#if (INCLUDE_uxTaskGetStackHighWaterMark == 1)

UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)
{
	TCB_t *pxTCB;
	uint8_t *pucEndOfStack;
	UBaseType_t uxReturn;

	pxTCB = prvGetTCBFromHandle(xTask);

#if portSTACK_GROWTH < 0
	{
		pucEndOfStack = (uint8_t *)pxTCB->pxStack;
	}
#else
	{
		pucEndOfStack = (uint8_t *)pxTCB->pxEndOfStack;
	}
#endif

	uxReturn = (UBaseType_t)prvTaskCheckFreeStackSpace(pucEndOfStack);

	return uxReturn;
}

#endif /* INCLUDE_uxTaskGetStackHighWaterMark */
/*-----------------------------------------------------------*/

#if (INCLUDE_vTaskDelete == 1)

static void prvDeleteTCB(TCB_t *pxTCB)
{
	/* This call is required specifically for the TriCore port.  It must be
		above the vPortFree() calls.  The call is also used by ports/demos that
		want to allocate and clean RAM statically. */
	portCLEAN_UP_TCB(pxTCB);

/* Free up the memory allocated by the scheduler for the task.  It is up
		to the task to free any memory allocated at the application level. */
#if (configUSE_NEWLIB_REENTRANT == 1)
	{
		_reclaim_reent(&(pxTCB->xNewLib_reent));
	}
#endif /* configUSE_NEWLIB_REENTRANT */

#if ((configSUPPORT_DYNAMIC_ALLOCATION == 1) && (configSUPPORT_STATIC_ALLOCATION == 0) && (portUSING_MPU_WRAPPERS == 0))
	{
		/* The task can only have been allocated dynamically - free both
			the stack and TCB. */
		vPortFree(pxTCB->pxStack);
		vPortFree(pxTCB);
	}
#elif (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1)
	{
		/* The task could have been allocated statically or dynamically, so
			check what was statically allocated before trying to free the
			memory. */
		if (pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB)
		{
			/* Both the stack and TCB were allocated dynamically, so both
				must be freed. */
			vPortFree(pxTCB->pxStack);
			vPortFree(pxTCB);
		}
		else if (pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY)
		{
			/* Only the stack was statically allocated, so the TCB is the
				only memory that must be freed. */
			vPortFree(pxTCB);
		}
		else
		{
			/* Neither the stack nor the TCB were allocated dynamically, so
				nothing needs to be freed. */
			configASSERT(pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB)
				mtCOVERAGE_TEST_MARKER();
		}
	}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}

#endif /* INCLUDE_vTaskDelete */
/*-----------------------------------------------------------*/

// 重置xNextTaskUnblockTime，交换延时列表时调用
static void prvResetNextTaskUnblockTime(void)
{
	TCB_t *pxTCB;

	if (listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE)
	{
		// 当前延时列表是空的，设置为最大值，使得if(xTickCount >= xNextTaskUnblockTime)无法通过，直到延迟列表中有一个项目
		xNextTaskUnblockTime = portMAX_DELAY;
	}
	else
	{
		// 当前延时列表不为空，从延时列表中取值后赋值
		(pxTCB) = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);
		xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE(&((pxTCB)->xStateListItem));
	}
}
/*-----------------------------------------------------------*/

#if ((INCLUDE_xTaskGetCurrentTaskHandle == 1) || (configUSE_MUTEXES == 1))

TaskHandle_t xTaskGetCurrentTaskHandle(void)
{
	TaskHandle_t xReturn;

	/* A critical section is not required as this is not called from
		an interrupt and the current TCB will always be the same for any
		individual execution thread. */
	xReturn = pxCurrentTCB;

	return xReturn;
}

#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */
/*-----------------------------------------------------------*/

// 获取任务调度状态，比较好理解
#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))

BaseType_t xTaskGetSchedulerState(void)
{
	BaseType_t xReturn;

	if (xSchedulerRunning == pdFALSE)
	{
		xReturn = taskSCHEDULER_NOT_STARTED;
	}
	else
	{
		if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
		{
			xReturn = taskSCHEDULER_RUNNING;
		}
		else
		{
			xReturn = taskSCHEDULER_SUSPENDED;
		}
	}

	return xReturn;
}

#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */
/*-----------------------------------------------------------*/

#if (configUSE_MUTEXES == 1)

void vTaskPriorityInherit(TaskHandle_t const pxMutexHolder)
{
	TCB_t *const pxTCB = (TCB_t *)pxMutexHolder;

	/* If the mutex was given back by an interrupt while the queue was
		locked then the mutex holder might now be NULL. */
	if (pxMutexHolder != NULL)
	{
		/* If the holder of the mutex has a priority below the priority of
			the task attempting to obtain the mutex then it will temporarily
			inherit the priority of the task attempting to obtain the mutex. */
		if (pxTCB->uxPriority < pxCurrentTCB->uxPriority)
		{
			/* Adjust the mutex holder state to account for its new
				priority.  Only reset the event list item value if the value is
				not	being used for anything else. */
			if ((listGET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem)) & taskEVENT_LIST_ITEM_VALUE_IN_USE) == 0UL)
			{
				listSET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem), (TickType_t)configMAX_PRIORITIES - (TickType_t)pxCurrentTCB->uxPriority); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
			}

			/* If the task being modified is in the ready state it will need
				to be moved into a new list. */
			if (listIS_CONTAINED_WITHIN(&(pxReadyTasksLists[pxTCB->uxPriority]), &(pxTCB->xStateListItem)) != pdFALSE)
			{
				if (uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
				{
					taskRESET_READY_PRIORITY(pxTCB->uxPriority);
				}

				/* Inherit the priority before being moved into the new list. */
				pxTCB->uxPriority = pxCurrentTCB->uxPriority;
				prvAddTaskToReadyList(pxTCB);
			}
			else
			{
				/* Just inherit the priority. */
				pxTCB->uxPriority = pxCurrentTCB->uxPriority;
			}

			traceTASK_PRIORITY_INHERIT(pxTCB, pxCurrentTCB->uxPriority);
		}
	}
}

#endif /* configUSE_MUTEXES */
/*-----------------------------------------------------------*/

#if (configUSE_MUTEXES == 1)

BaseType_t xTaskPriorityDisinherit(TaskHandle_t const pxMutexHolder)
{
	TCB_t *const pxTCB = (TCB_t *)pxMutexHolder;
	BaseType_t xReturn = pdFALSE;

	if (pxMutexHolder != NULL)
	{
		/* A task can only have an inherited priority if it holds the mutex.
			If the mutex is held by a task then it cannot be given from an
			interrupt, and if a mutex is given by the holding task then it must
			be the running state task. */
		configASSERT(pxTCB == pxCurrentTCB);

		configASSERT(pxTCB->uxMutexesHeld);
		(pxTCB->uxMutexesHeld)--;

		/* Has the holder of the mutex inherited the priority of another
			task? */
		if (pxTCB->uxPriority != pxTCB->uxBasePriority)
		{
			/* Only disinherit if no other mutexes are held. */
			if (pxTCB->uxMutexesHeld == (UBaseType_t)0)
			{
				/* A task can only have an inherited priority if it holds
					the mutex.  If the mutex is held by a task then it cannot be
					given from an interrupt, and if a mutex is given by the
					holding	task then it must be the running state task.  Remove
					the	holding task from the ready	list. */
				if (uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
				{
					taskRESET_READY_PRIORITY(pxTCB->uxPriority);
				}

				/* Disinherit the priority before adding the task into the
					new	ready list. */
				traceTASK_PRIORITY_DISINHERIT(pxTCB, pxTCB->uxBasePriority);
				pxTCB->uxPriority = pxTCB->uxBasePriority;

				/* Reset the event list item value.  It cannot be in use for
					any other purpose if this task is running, and it must be
					running to give back the mutex. */
				listSET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem), (TickType_t)configMAX_PRIORITIES - (TickType_t)pxTCB->uxPriority); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
				prvAddTaskToReadyList(pxTCB);

				/* Return true to indicate that a context switch is required.
					This is only actually required in the corner case whereby
					multiple mutexes were held and the mutexes were given back
					in an order different to that in which they were taken.
					If a context switch did not occur when the first mutex was
					returned, even if a task was waiting on it, then a context
					switch should occur when the last mutex is returned whether
					a task is waiting on it or not. */
				xReturn = pdTRUE;
			}
		}
	}

	return xReturn;
}

#endif /* configUSE_MUTEXES */
/*-----------------------------------------------------------*/

#if (portCRITICAL_NESTING_IN_TCB == 1)

void vTaskEnterCritical(void)
{
	portDISABLE_INTERRUPTS();

	if (xSchedulerRunning != pdFALSE)
	{
		(pxCurrentTCB->uxCriticalNesting)++;

		/* This is not the interrupt safe version of the enter critical
			function so	assert() if it is being called from an interrupt
			context.  Only API functions that end in "FromISR" can be used in an
			interrupt.  Only assert if the critical nesting count is 1 to
			protect against recursive calls if the assert function also uses a
			critical section. */
		if (pxCurrentTCB->uxCriticalNesting == 1)
		{
			portASSERT_IF_IN_ISR();
		}
	}
}

#endif /* portCRITICAL_NESTING_IN_TCB */
/*-----------------------------------------------------------*/

#if (portCRITICAL_NESTING_IN_TCB == 1)

void vTaskExitCritical(void)
{
	if (xSchedulerRunning != pdFALSE)
	{
		if (pxCurrentTCB->uxCriticalNesting > 0U)
		{
			(pxCurrentTCB->uxCriticalNesting)--;

			if (pxCurrentTCB->uxCriticalNesting == 0U)
			{
				portENABLE_INTERRUPTS();
			}
		}
	}
}

#endif /* portCRITICAL_NESTING_IN_TCB */
/*-----------------------------------------------------------*/

#if ((configUSE_TRACE_FACILITY == 1) && (configUSE_STATS_FORMATTING_FUNCTIONS > 0))

static char *prvWriteNameToBuffer(char *pcBuffer, const char *pcTaskName)
{
	size_t x;

	/* Start by copying the entire string. */
	strcpy(pcBuffer, pcTaskName);

	/* Pad the end of the string with spaces to ensure columns line up when
		printed out. */
	for (x = strlen(pcBuffer); x < (size_t)(configMAX_TASK_NAME_LEN - 1); x++)
	{
		pcBuffer[x] = ' ';
	}

	/* Terminate. */
	pcBuffer[x] = 0x00;

	/* Return the new end of string. */
	return &(pcBuffer[x]);
}

#endif /* ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */
/*-----------------------------------------------------------*/

#if ((configUSE_TRACE_FACILITY == 1) && (configUSE_STATS_FORMATTING_FUNCTIONS > 0))

void vTaskList(char *pcWriteBuffer)
{
	TaskStatus_t *pxTaskStatusArray;
	volatile UBaseType_t uxArraySize, x;
	char cStatus;

	/* Make sure the write buffer does not contain a string. */
	*pcWriteBuffer = 0x00;

	/* Take a snapshot of the number of tasks in case it changes while this
		function is executing. */
	uxArraySize = uxCurrentNumberOfTasks;

	/* Allocate an array index for each task.  NOTE!  if
		configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will
		equate to NULL. */
	pxTaskStatusArray = pvPortMalloc(uxCurrentNumberOfTasks * sizeof(TaskStatus_t));

	if (pxTaskStatusArray != NULL)
	{
		/* Generate the (binary) data. */
		uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL);

		/* Create a human readable table from the binary data. */
		for (x = 0; x < uxArraySize; x++)
		{
			switch (pxTaskStatusArray[x].eCurrentState)
			{
			case eReady:
				cStatus = tskREADY_CHAR;
				break;

			case eBlocked:
				cStatus = tskBLOCKED_CHAR;
				break;

			case eSuspended:
				cStatus = tskSUSPENDED_CHAR;
				break;

			case eDeleted:
				cStatus = tskDELETED_CHAR;
				break;

			default: /* Should not get here, but it is included
										to prevent static checking errors. */
				cStatus = 0x00;
				break;
			}

			/* Write the task name to the string, padding with spaces so it
				can be printed in tabular form more easily. */
			pcWriteBuffer = prvWriteNameToBuffer(pcWriteBuffer, pxTaskStatusArray[x].pcTaskName);

			/* Write the rest of the string. */
			sprintf(pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, (unsigned int)pxTaskStatusArray[x].uxCurrentPriority, (unsigned int)pxTaskStatusArray[x].usStackHighWaterMark, (unsigned int)pxTaskStatusArray[x].xTaskNumber);
			pcWriteBuffer += strlen(pcWriteBuffer);
		}

		/* Free the array again.  NOTE!  If configSUPPORT_DYNAMIC_ALLOCATION
			is 0 then vPortFree() will be #defined to nothing. */
		vPortFree(pxTaskStatusArray);
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */
/*----------------------------------------------------------*/

#if ((configGENERATE_RUN_TIME_STATS == 1) && (configUSE_STATS_FORMATTING_FUNCTIONS > 0))

void vTaskGetRunTimeStats(char *pcWriteBuffer)
{
	TaskStatus_t *pxTaskStatusArray;
	volatile UBaseType_t uxArraySize, x;
	uint32_t ulTotalTime, ulStatsAsPercentage;

#if (configUSE_TRACE_FACILITY != 1)
	{
#error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats().
	}
#endif

	/* Make sure the write buffer does not contain a string. */
	*pcWriteBuffer = 0x00;

	/* Take a snapshot of the number of tasks in case it changes while this
		function is executing. */
	uxArraySize = uxCurrentNumberOfTasks;

	/* Allocate an array index for each task.  NOTE!  If
		configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will
		equate to NULL. */
	pxTaskStatusArray = pvPortMalloc(uxCurrentNumberOfTasks * sizeof(TaskStatus_t));

	if (pxTaskStatusArray != NULL)
	{
		/* Generate the (binary) data. */
		uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalTime);

		/* For percentage calculations. */
		ulTotalTime /= 100UL;

		/* Avoid divide by zero errors. */
		if (ulTotalTime > 0)
		{
			/* Create a human readable table from the binary data. */
			for (x = 0; x < uxArraySize; x++)
			{
				/* What percentage of the total run time has the task used?
					This will always be rounded down to the nearest integer.
					ulTotalRunTimeDiv100 has already been divided by 100. */
				ulStatsAsPercentage = pxTaskStatusArray[x].ulRunTimeCounter / ulTotalTime;

				/* Write the task name to the string, padding with
					spaces so it can be printed in tabular form more
					easily. */
				pcWriteBuffer = prvWriteNameToBuffer(pcWriteBuffer, pxTaskStatusArray[x].pcTaskName);

				if (ulStatsAsPercentage > 0UL)
				{
#ifdef portLU_PRINTF_SPECIFIER_REQUIRED
					{
						sprintf(pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[x].ulRunTimeCounter, ulStatsAsPercentage);
					}
#else
					{
						/* sizeof( int ) == sizeof( long ) so a smaller
							printf() library can be used. */
						sprintf(pcWriteBuffer, "\t%u\t\t%u%%\r\n", (unsigned int)pxTaskStatusArray[x].ulRunTimeCounter, (unsigned int)ulStatsAsPercentage);
					}
#endif
				}
				else
				{
/* If the percentage is zero here then the task has
						consumed less than 1% of the total run time. */
#ifdef portLU_PRINTF_SPECIFIER_REQUIRED
					{
						sprintf(pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[x].ulRunTimeCounter);
					}
#else
					{
						/* sizeof( int ) == sizeof( long ) so a smaller
							printf() library can be used. */
						sprintf(pcWriteBuffer, "\t%u\t\t<1%%\r\n", (unsigned int)pxTaskStatusArray[x].ulRunTimeCounter);
					}
#endif
				}

				pcWriteBuffer += strlen(pcWriteBuffer);
			}
		}

		/* Free the array again.  NOTE!  If configSUPPORT_DYNAMIC_ALLOCATION
			is 0 then vPortFree() will be #defined to nothing. */
		vPortFree(pxTaskStatusArray);
	}
}

#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */
/*-----------------------------------------------------------*/

// 重置事件列表项，用途？
TickType_t uxTaskResetEventItemValue(void)
{
	TickType_t uxReturn;

	uxReturn = listGET_LIST_ITEM_VALUE(&(pxCurrentTCB->xEventListItem));

	/* Reset the event list item to its normal value - so it can be used with queues and semaphores. */
	listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xEventListItem), ((TickType_t)configMAX_PRIORITIES - (TickType_t)pxCurrentTCB->uxPriority)); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

	return uxReturn;
}
/*-----------------------------------------------------------*/

#if (configUSE_MUTEXES == 1)

void *pvTaskIncrementMutexHeldCount(void)
{
	/* If xSemaphoreCreateMutex() is called before any tasks have been created
		then pxCurrentTCB will be NULL. */
	if (pxCurrentTCB != NULL)
	{
		(pxCurrentTCB->uxMutexesHeld)++;
	}

	return pxCurrentTCB;
}

#endif /* configUSE_MUTEXES */
/*-----------------------------------------------------------*/

#if (configUSE_TASK_NOTIFICATIONS == 1)

uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait)
{
	uint32_t ulReturn;

	taskENTER_CRITICAL();
	{
		/* Only block if the notification count is not already non-zero. */
		if (pxCurrentTCB->ulNotifiedValue == 0UL)
		{
			/* Mark this task as waiting for a notification. */
			pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

			if (xTicksToWait > (TickType_t)0)
			{
				prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
				traceTASK_NOTIFY_TAKE_BLOCK();

				/* All ports are written to allow a yield in a critical
					section (some will yield immediately, others wait until the
					critical section exits) - but it is not something that
					application code should ever do. */
				portYIELD_WITHIN_API();
			}
		}
	}
	taskEXIT_CRITICAL();

	taskENTER_CRITICAL();
	{
		traceTASK_NOTIFY_TAKE();
		ulReturn = pxCurrentTCB->ulNotifiedValue;

		if (ulReturn != 0UL)
		{
			if (xClearCountOnExit != pdFALSE)
			{
				pxCurrentTCB->ulNotifiedValue = 0UL;
			}
			else
			{
				pxCurrentTCB->ulNotifiedValue = ulReturn - 1;
			}
		}

		pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	taskEXIT_CRITICAL();

	return ulReturn;
}

#endif /* configUSE_TASK_NOTIFICATIONS */
/*-----------------------------------------------------------*/

#if (configUSE_TASK_NOTIFICATIONS == 1)

BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait)
{
	BaseType_t xReturn;

	taskENTER_CRITICAL();
	{
		/* Only block if a notification is not already pending. */
		if (pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED)
		{
			/* Clear bits in the task's notification value as bits may get
				set	by the notifying task or interrupt.  This can be used to
				clear the value to zero. */
			pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;

			/* Mark this task as waiting for a notification. */
			pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

			if (xTicksToWait > (TickType_t)0)
			{
				prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
				traceTASK_NOTIFY_WAIT_BLOCK();

				/* All ports are written to allow a yield in a critical
					section (some will yield immediately, others wait until the
					critical section exits) - but it is not something that
					application code should ever do. */
				portYIELD_WITHIN_API();
			}
		}
	}
	taskEXIT_CRITICAL();

	taskENTER_CRITICAL();
	{
		traceTASK_NOTIFY_WAIT();

		if (pulNotificationValue != NULL)
		{
			/* Output the current notification value, which may or may not
				have changed. */
			*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
		}

		/* If ucNotifyValue is set then either the task never entered the
			blocked state (because a notification was already pending) or the
			task unblocked because of a notification.  Otherwise the task
			unblocked because of a timeout. */
		if (pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION)
		{
			/* A notification was not received. */
			xReturn = pdFALSE;
		}
		else
		{
			/* A notification was already pending or a notification was
				received while the task was waiting. */
			pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
			xReturn = pdTRUE;
		}

		pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	taskEXIT_CRITICAL();

	return xReturn;
}

#endif /* configUSE_TASK_NOTIFICATIONS */
/*-----------------------------------------------------------*/

#if (configUSE_TASK_NOTIFICATIONS == 1)

BaseType_t xTaskGenericNotify(TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue)
{
	TCB_t *pxTCB;
	BaseType_t xReturn = pdPASS;
	uint8_t ucOriginalNotifyState;

	configASSERT(xTaskToNotify);
	pxTCB = (TCB_t *)xTaskToNotify;

	taskENTER_CRITICAL();
	{
		if (pulPreviousNotificationValue != NULL)
		{
			*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
		}

		ucOriginalNotifyState = pxTCB->ucNotifyState;

		pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;

		switch (eAction)
		{
		case eSetBits:
			pxTCB->ulNotifiedValue |= ulValue;
			break;

		case eIncrement:
			(pxTCB->ulNotifiedValue)++;
			break;

		case eSetValueWithOverwrite:
			pxTCB->ulNotifiedValue = ulValue;
			break;

		case eSetValueWithoutOverwrite:
			if (ucOriginalNotifyState != taskNOTIFICATION_RECEIVED)
			{
				pxTCB->ulNotifiedValue = ulValue;
			}
			else
			{
				/* The value could not be written to the task. */
				xReturn = pdFAIL;
			}
			break;

		case eNoAction:
			/* The task is being notified without its notify value being
					updated. */
			break;
		}

		traceTASK_NOTIFY();

		/* If the task is in the blocked state specifically to wait for a
			notification then unblock it now. */
		if (ucOriginalNotifyState == taskWAITING_NOTIFICATION)
		{
			(void)uxListRemove(&(pxTCB->xStateListItem));
			prvAddTaskToReadyList(pxTCB);

			/* The task should not have been on an event list. */
			configASSERT(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) == NULL);

#if (configUSE_TICKLESS_IDLE != 0)
			{
				/* If a task is blocked waiting for a notification then
					xNextTaskUnblockTime might be set to the blocked task's time
					out time.  If the task is unblocked for a reason other than
					a timeout xNextTaskUnblockTime is normally left unchanged,
					because it will automatically get reset to a new value when
					the tick count equals xNextTaskUnblockTime.  However if
					tickless idling is used it might be more important to enter
					sleep mode at the earliest possible time - so reset
					xNextTaskUnblockTime here to ensure it is updated at the
					earliest possible time. */
				prvResetNextTaskUnblockTime();
			}
#endif

			if (pxTCB->uxPriority > pxCurrentTCB->uxPriority)
			{
				/* The notified task has a priority above the currently
					executing task so a yield is required. */
				taskYIELD_IF_USING_PREEMPTION();
			}
		}
	}
	taskEXIT_CRITICAL();

	return xReturn;
}

#endif /* configUSE_TASK_NOTIFICATIONS */
/*-----------------------------------------------------------*/

#if (configUSE_TASK_NOTIFICATIONS == 1)

BaseType_t xTaskGenericNotifyFromISR(TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken)
{
	TCB_t *pxTCB;
	uint8_t ucOriginalNotifyState;
	BaseType_t xReturn = pdPASS;
	UBaseType_t uxSavedInterruptStatus;

	configASSERT(xTaskToNotify);

	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	pxTCB = (TCB_t *)xTaskToNotify;

	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		if (pulPreviousNotificationValue != NULL)
		{
			*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
		}

		ucOriginalNotifyState = pxTCB->ucNotifyState;
		pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;

		switch (eAction)
		{
		case eSetBits:
			pxTCB->ulNotifiedValue |= ulValue;
			break;

		case eIncrement:
			(pxTCB->ulNotifiedValue)++;
			break;

		case eSetValueWithOverwrite:
			pxTCB->ulNotifiedValue = ulValue;
			break;

		case eSetValueWithoutOverwrite:
			if (ucOriginalNotifyState != taskNOTIFICATION_RECEIVED)
			{
				pxTCB->ulNotifiedValue = ulValue;
			}
			else
			{
				/* The value could not be written to the task. */
				xReturn = pdFAIL;
			}
			break;

		case eNoAction:
			/* The task is being notified without its notify value being
					updated. */
			break;
		}

		traceTASK_NOTIFY_FROM_ISR();

		/* If the task is in the blocked state specifically to wait for a
			notification then unblock it now. */
		if (ucOriginalNotifyState == taskWAITING_NOTIFICATION)
		{
			/* The task should not have been on an event list. */
			configASSERT(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) == NULL);

			if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
			{
				(void)uxListRemove(&(pxTCB->xStateListItem));
				prvAddTaskToReadyList(pxTCB);
			}
			else
			{
				/* The delayed and ready lists cannot be accessed, so hold
					this task pending until the scheduler is resumed. */
				vListInsertEnd(&(xPendingReadyList), &(pxTCB->xEventListItem));
			}

			if (pxTCB->uxPriority > pxCurrentTCB->uxPriority)
			{
				/* The notified task has a priority above the currently
					executing task so a yield is required. */
				if (pxHigherPriorityTaskWoken != NULL)
				{
					*pxHigherPriorityTaskWoken = pdTRUE;
				}
				else
				{
					/* Mark that a yield is pending in case the user is not
						using the "xHigherPriorityTaskWoken" parameter to an ISR
						safe FreeRTOS function. */
					xYieldPending = pdTRUE;
				}
			}
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

	return xReturn;
}

#endif /* configUSE_TASK_NOTIFICATIONS */

//
#if (configUSE_TASK_NOTIFICATIONS == 1)
void vTaskNotifyGiveFromISR(TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken)
{
	TCB_t *pxTCB;
	uint8_t ucOriginalNotifyState;
	UBaseType_t uxSavedInterruptStatus;

	configASSERT(xTaskToNotify);

	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	pxTCB = (TCB_t *)xTaskToNotify;

	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		ucOriginalNotifyState = pxTCB->ucNotifyState;
		pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;

		/* 'Giving' is equivalent to incrementing a count in a counting
			semaphore. */
		(pxTCB->ulNotifiedValue)++;

		traceTASK_NOTIFY_GIVE_FROM_ISR();

		/* If the task is in the blocked state specifically to wait for a
			notification then unblock it now. */
		if (ucOriginalNotifyState == taskWAITING_NOTIFICATION)
		{
			/* The task should not have been on an event list. */
			configASSERT(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) == NULL);

			if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
			{
				(void)uxListRemove(&(pxTCB->xStateListItem));
				prvAddTaskToReadyList(pxTCB);
			}
			else
			{
				/* The delayed and ready lists cannot be accessed, so hold
					this task pending until the scheduler is resumed. */
				vListInsertEnd(&(xPendingReadyList), &(pxTCB->xEventListItem));
			}

			if (pxTCB->uxPriority > pxCurrentTCB->uxPriority)
			{
				/* The notified task has a priority above the currently
					executing task so a yield is required. */
				if (pxHigherPriorityTaskWoken != NULL)
				{
					*pxHigherPriorityTaskWoken = pdTRUE;
				}
				else
				{
					/* Mark that a yield is pending in case the user is not
						using the "xHigherPriorityTaskWoken" parameter in an ISR
						safe FreeRTOS function. */
					xYieldPending = pdTRUE;
				}
			}
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}
#endif /* configUSE_TASK_NOTIFICATIONS */

/*-----------------------------------------------------------*/

#if (configUSE_TASK_NOTIFICATIONS == 1)

BaseType_t xTaskNotifyStateClear(TaskHandle_t xTask)
{
	TCB_t *pxTCB;
	BaseType_t xReturn;

	// 获取TCB
	pxTCB = prvGetTCBFromHandle(xTask);

	taskENTER_CRITICAL();
	{
		if (pxTCB->ucNotifyState == taskNOTIFICATION_RECEIVED)
		{
			pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
			xReturn = pdPASS;
		}
		else
		{
			xReturn = pdFAIL;
		}
	}
	taskEXIT_CRITICAL();

	return xReturn;
}

#endif /* configUSE_TASK_NOTIFICATIONS */
/*-----------------------------------------------------------*/

static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely)
{
	TickType_t xTimeToWake;
	const TickType_t xConstTickCount = xTickCount;

#if (INCLUDE_xTaskAbortDelay == 1)
	{
		pxCurrentTCB->ucDelayAborted = pdFALSE;
	}
#endif

	// 任务加到block列表前，要先从就绪列表移除
	if (uxListRemove(&(pxCurrentTCB->xStateListItem)) == (UBaseType_t)0)
	{
		// 当前任务在就绪列表中，所以不用检查(?)，直接修改uxTopReadyPriority
		portRESET_READY_PRIORITY(pxCurrentTCB->uxPriority, uxTopReadyPriority);
	}

#if (INCLUDE_vTaskSuspend == 1)
	{
		if ((xTicksToWait == portMAX_DELAY) && (xCanBlockIndefinitely != pdFALSE))
		{
			// 将任务添加到挂起列表，而不是延迟列表，以确保它不会被计时事件唤醒，它会一直阻塞
			vListInsertEnd(&xSuspendedTaskList, &(pxCurrentTCB->xStateListItem));
		}
		else
		{
			// 计算任务的唤醒时间，可能会溢出但是仍能保证正确
			xTimeToWake = xConstTickCount + xTicksToWait;

			// 列表项将按照唤醒时间排序插入列表
			listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem), xTimeToWake);

			if (xTimeToWake < xConstTickCount)
			{
				// 唤醒时间已经溢出了，需要将列表项放入溢出列表
				vListInsert(pxOverflowDelayedTaskList, &(pxCurrentTCB->xStateListItem));
			}
			else
			{
				// 唤醒时间没有溢出，放入延时列表
				vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));

				// 更新所有任务的xNextTaskUnblockTime
				if (xTimeToWake < xNextTaskUnblockTime)
				{
					xNextTaskUnblockTime = xTimeToWake;
				}
			}
		}
	}
#else  /* INCLUDE_vTaskSuspend */
	{
		// 
		xTimeToWake = xConstTickCount + xTicksToWait;

		// 
		listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem), xTimeToWake);

		if (xTimeToWake < xConstTickCount)
		{
			// 
			vListInsert(pxOverflowDelayedTaskList, &(pxCurrentTCB->xStateListItem));
		}
		else
		{
			// 
			vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));

			// 
			if (xTimeToWake < xNextTaskUnblockTime)
			{
				xNextTaskUnblockTime = xTimeToWake;
			}
		}

		// 抑制编译器警告
		(void)xCanBlockIndefinitely;
	}
#endif /* INCLUDE_vTaskSuspend */
}

#ifdef FREERTOS_MODULE_TEST
#include "tasks_test_access_functions.h"
#endif
